Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[16.0] [MIG] product_contract #959

Open
wants to merge 148 commits into
base: 16.0
Choose a base branch
from

Conversation

Rad0van
Copy link
Sponsor Contributor

@Rad0van Rad0van commented Jun 8, 2023

Fairly standard, had only to update one compute method name.

@Rad0van Rad0van force-pushed the 16.0-mig-product_contract branch from 84dcd76 to 54fad5f Compare June 8, 2023 17:26
@rousseldenis
Copy link
Sponsor Contributor

/ocabot migration product_contract

Copy link
Sponsor Contributor

@rousseldenis rousseldenis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review

product_contract/models/contract_line.py Outdated Show resolved Hide resolved
product_contract/models/contract_line.py Outdated Show resolved Hide resolved
product_contract/models/contract_line.py Show resolved Hide resolved
product_contract/models/product_template.py Outdated Show resolved Hide resolved
product_contract/models/sale_order.py Outdated Show resolved Hide resolved
product_contract/models/sale_order_line.py Outdated Show resolved Hide resolved
@Rad0van
Copy link
Sponsor Contributor Author

Rad0van commented Jun 9, 2023

@rousseldenis there is a typo in sale_order.py:

    @api.constrains("state")
    def _check_contact_is_not_terminated(self):

It should have been _check_contRact_is_not_terminated Should that be fixed? If so, should it be fixed in migration or in separate PR?

)
return date_end

@api.depends("product_id")
Copy link
Sponsor Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rousseldenis, @sbejaoui, @pedrobaeza, @sbidoul maybe you could help me here please. I am trying to use this module in e-shop. However when I purchase the product, Contract is created but all the relevant fields are empty. What I found out is that this method _compute_auto_renew is not called in such scenario. I'm scratching my head on why is it never called and how to solve this in some good and elegant way. Any help / advice is really appreciated.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Rad0van ,
I think the issue is that all the fields computed by this method have a default value.
What happens at record creation is that get assigned their default value so they don't need to be computed;
The computation method triggers-in, correctly, next time the observed field (product_id) changes.

This issue doesn't happen when a user manually creates a Sale Order because the computation method is immediately triggered when product_id changes (as the computation method is also part of the onchange workflow).

Besides causing this issue, a default value on a computed field doesn't make much sense in my opinion. I suggest you remove all defaults from field definition and maybe add them in the computation method itself.

Copy link
Sponsor Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @espo-tony ,

not really sure I follow what you suggest here because the issue is as you point out that when Sale Order is created directly, the computation method is not called. I have created a hacky solution by creating glue module between product_contract and website_sale where I basically reproduce the whole _compute_auto_renew method except for setting the quantity. It goes like this:

class SaleOrder(models.Model):
    _inherit = "sale.order"

    def _cart_update_order_line(self, product_id, quantity, order_line, **kwargs):
        """
        This ensures that when contract product is put into basket in e-commerce
        all necessary values are set
        """
        order_line = super()._cart_update_order_line(
            product_id, quantity, order_line, **kwargs
        )

        """
        This is (apart from commented out line) 1:1 copy of _compute_auto_renew method
        from product_contract module to ensure we get the defaults configured
        """
        if order_line.product_id.is_contract:
            # order_line.product_uom_qty = order_line.product_id.default_qty
            order_line.recurring_rule_type = order_line.product_id.recurring_rule_type
            order_line.recurring_invoicing_type = (
                order_line.product_id.recurring_invoicing_type
            )
            order_line.date_start = order_line.date_start or fields.Date.today()

            order_line.date_end = order_line._get_date_end()
            order_line.is_auto_renew = order_line.product_id.is_auto_renew
            if order_line.is_auto_renew:
                order_line.auto_renew_interval = (
                    order_line.product_id.auto_renew_interval
                )
                order_line.auto_renew_rule_type = (
                    order_line.product_id.auto_renew_rule_type
                )

        return order_line

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Rad0van
When a Sale Order is created directly, the computation method is not called because of the default values. If you remove them, the computation method will be called even when a Sale Order is created directly. I tested this solution locally and it worked for me.

Copy link
Sponsor Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Rad0van When a Sale Order is created directly, the computation method is not called because of the default values. If you remove them, the computation method will be called even when a Sale Order is created directly. I tested this solution locally and it worked for me.

Oh I see now! But that is quite a big change which I think should be approved by more senior people.
Also I would be interested to get to know the inner mechanics of this. I.e. why the @api.depends is called when the default values are not present? It only depends on product_id Does anyone know the explanation?

Copy link
Member

@Abranes Abranes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change this attrs to invisible to fix the view problems

image
Image from runboat

product_contract/views/sale_order.xml Outdated Show resolved Hide resolved
product_contract/views/sale_order.xml Outdated Show resolved Hide resolved
product_contract/views/sale_order.xml Outdated Show resolved Hide resolved
product_contract/views/sale_order.xml Outdated Show resolved Hide resolved
Ted Salmon and others added 17 commits November 9, 2023 15:05
* Add contract functionality to `product.templates`
* Add logic to create contracts from `sale.order` that contains contract products.
* Change the method called in the view
* Complete the create_invoice method
* Bump version + authoring
* Correct bad call of method
  Small Documentation
* Add super call in python test
* FIX bad field names causing bad quantities in sale.order.line
- On Sale Order confirmation, a contract is created for each contract template used on sale order lines
- A not finished contract can be mentioned on sale order line
- A sale order line linked to a contract will update it and don't create a new one if it had the same template
recurring_next_date should be computed by contract line to get default value
- Sale order line for contract product pass to nothing to invoice on order confirmation
- Contract Invoices are linked to sale order line
@Rad0van Rad0van force-pushed the 16.0-mig-product_contract branch 2 times, most recently from f9f7f4d to 0679da3 Compare November 10, 2023 07:42
Copy link
Contributor

@JorgeQuinteros JorgeQuinteros left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM Functional Review

@gregory-moka
Copy link
Contributor

Hello,

tried to add a contract line on a Sale Order. As I want to confirm it, this error message is displayed :
image
=> Maybe the contract type should be required as we configure the product

Copy link
Member

@Abranes Abranes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review

LGTM!

@DorianMAG
Copy link

Thx for this work !
Tested on runboat, LGTM

Copy link

@navarrorico navarrorico left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

@Rad0van
Copy link
Sponsor Contributor Author

Rad0van commented Feb 10, 2024

@remi-filament seems @Abranes is right. In the commit #1019 the analytic_account_id was indeed removed...

@remi-filament
Copy link
Contributor

@remi-filament seems @Abranes is right. In the commit #1019 the analytic_account_id was indeed removed...

Ah yes, my bad I checked contract code on your branch and not on upstream when your branch is outdated for that topic...
It makes sense that it is removed, there is still a group_id on contract but it does not seem to be used anywhere so better to not use analytic account anymore.

You should then rebase this PR on OCA v16 branch and squash your 3 last commits in one before this PR can be merged.

contract = contract_model.create(
rec._prepare_contract_value(contract_template)
)
contracts.append(contract)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When performing a create(), the created object 'contract.contract(xxx,)' is returned. When appending to the list named 'contracts', you should indicate performing a contract.id to create a list of contract identifiers. If this is not done, when performing a contract_model.browse(contracts), it returns contract.contract(contract.contract(xxx,),), which is incorrect. From what I see, it should return contract.contract(xxx,).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Rad0van can you answer this comment?

Copy link
Contributor

@aliciagaarzo aliciagaarzo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Copy link
Contributor

@aliciagaarzo aliciagaarzo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review to cover all tests. Regards.

"payment_term_id": self.payment_term_id.id,
"fiscal_position_id": self.fiscal_position_id.id,
"invoice_partner_id": self.partner_invoice_id.id,
"line_recurrence": self.partner_invoice_id.id,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the line_recurrence is the self.partner_invoice_id?

Copy link
Sponsor Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the line_recurrence is the self.partner_invoice_id?

Funny, it's the same as in original module:

"payment_term_id": self.payment_term_id.id,
"fiscal_position_id": self.fiscal_position_id.id,
"invoice_partner_id": self.partner_invoice_id.id,
"line_recurrence": self.partner_invoice_id.id,

But probably needs fixing as well.

line_to_update_contract = rec.order_line.filtered(
lambda r: r.contract_id
and r.product_id.is_contract
and r
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should check that r exists at the beggining of the function, no? Otherwise, when performing r.contract_id it will fail.

Copy link
Sponsor Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should check that r exists at the beggining of the function, no? Otherwise, when performing r.contract_id it will fail.

It's just formatting that's misleading you. Check the following line ;-)

"line_recurrence": self.partner_invoice_id.id,
}

def action_create_contract(self):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I change the product_uom_qty before creating the contract, when creating the contract the quantity is doesn't match.

image
image

Copy link

@mrcast mrcast left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functional test ok!

@jaenbox
Copy link
Contributor

jaenbox commented May 8, 2024

Hello @Rad0van, I just wanted to ask if you were planning to continue with the PR. I see that you have some unanswered comments, and I was wondering if I can proceed with the PR of the product_contract module. If it's not a problem!

@Rad0van
Copy link
Sponsor Contributor Author

Rad0van commented May 8, 2024

Hello @Rad0van, I just wanted to ask if you were planning to continue with the PR. I see that you have some unanswered comments, and I was wondering if I can proceed with the PR of the product_contract module. If it's not a problem!

Was quite busy but planning to finish this one soon.

@pedrobaeza
Copy link
Member

@rousseldenis can you update your review?

@Rad0van
Copy link
Sponsor Contributor Author

Rad0van commented Jun 21, 2024

I have carried over changes done in #1083 so now these are 1:1 but also fix a typo in constrain method name.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet