From 382f30b248d76ee18deeffb978d2275477bf1651 Mon Sep 17 00:00:00 2001 From: David Beal Date: Wed, 3 Apr 2024 18:09:40 +0200 Subject: [PATCH 1/2] wip --- purchase_update_proposal/README.rst | 34 ++++--- purchase_update_proposal/__manifest__.py | 2 +- purchase_update_proposal/i18n/fr.po | 12 --- .../i18n/purchase_update_proposal.pot | 12 --- .../models/purchase_order.py | 93 +++++-------------- .../models/purchase_order_line.py | 24 +---- .../models/purchase_proposal.py | 7 +- .../static/description/index.html | 45 ++++----- .../tests/test_proposal.py | 37 ++++---- .../tests/test_proposal_with_picking.py | 45 +++++---- .../views/purchase_line_proposal.xml | 4 +- .../views/purchase_order.xml | 9 +- .../views/supplier_purchase_order.xml | 45 ++------- 13 files changed, 133 insertions(+), 236 deletions(-) diff --git a/purchase_update_proposal/README.rst b/purchase_update_proposal/README.rst index 0d570783c3b..2c5323363a5 100644 --- a/purchase_update_proposal/README.rst +++ b/purchase_update_proposal/README.rst @@ -2,10 +2,13 @@ Purchase Update Proposal ======================== -.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:1980276aaf72580b8f7903e0595dcadfc4df2160e9d7633cc31276cfca1d457d + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png :target: https://odoo-community.org/page/development-status @@ -14,16 +17,16 @@ Purchase Update Proposal :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fpurchase--workflow-lightgray.png?logo=github - :target: https://github.com/OCA/purchase-workflow/tree/8.0/purchase_update_proposal + :target: https://github.com/OCA/purchase-workflow/tree/16.0/purchase_update_proposal :alt: OCA/purchase-workflow .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/purchase-workflow-8-0/purchase-workflow-8-0-purchase_update_proposal + :target: https://translation.odoo-community.org/projects/purchase-workflow-16-0/purchase-workflow-16-0-purchase_update_proposal :alt: Translate me on Weblate -.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/142/8.0 - :alt: Try me on Runbot +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/purchase-workflow&target_branch=16.0 + :alt: Try me on Runboat -|badge1| |badge2| |badge3| |badge4| |badge5| +|badge1| |badge2| |badge3| |badge4| |badge5| Allow for any user with only read only grants on Purchase Orders to suggest/ask for modification. @@ -49,13 +52,13 @@ Usage Go to the supplier menu This document is modified by the supplier with specific right on its own purchase document -.. figure:: https://raw.githubusercontent.com/OCA/purchase-workflow/8.0/purchase_update_proposal/static/description/supplier.png +.. figure:: https://raw.githubusercontent.com/OCA/purchase-workflow/16.0/purchase_update_proposal/static/description/supplier.png This document is modified by the purchaser pending approbation -.. figure:: https://raw.githubusercontent.com/OCA/purchase-workflow/8.0/purchase_update_proposal/static/description/purchaser.png +.. figure:: https://raw.githubusercontent.com/OCA/purchase-workflow/16.0/purchase_update_proposal/static/description/purchaser.png Features: @@ -72,8 +75,8 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -101,11 +104,14 @@ promote its widespread use. .. |maintainer-bealdav| image:: https://github.com/bealdav.png?size=40px :target: https://github.com/bealdav :alt: bealdav +.. |maintainer-FranzPoize| image:: https://github.com/FranzPoize.png?size=40px + :target: https://github.com/FranzPoize + :alt: FranzPoize -Current `maintainer `__: +Current `maintainers `__: -|maintainer-bealdav| +|maintainer-bealdav| |maintainer-FranzPoize| -This module is part of the `OCA/purchase-workflow `_ project on GitHub. +This module is part of the `OCA/purchase-workflow `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/purchase_update_proposal/__manifest__.py b/purchase_update_proposal/__manifest__.py index fa8fc0a569e..5a65910d684 100644 --- a/purchase_update_proposal/__manifest__.py +++ b/purchase_update_proposal/__manifest__.py @@ -25,5 +25,5 @@ ], "installable": True, "development_status": "Alpha", - "maintainers": ["bealdav"], + "maintainers": ["bealdav", "FranzPoize"], } diff --git a/purchase_update_proposal/i18n/fr.po b/purchase_update_proposal/i18n/fr.po index 9c7d2c272e8..dd5b884bda1 100644 --- a/purchase_update_proposal/i18n/fr.po +++ b/purchase_update_proposal/i18n/fr.po @@ -60,12 +60,6 @@ msgstr "Annulé" msgid "Check price on proposal" msgstr "Vérifier le prix lors de la proposition d'achat" -#. module: purchase_update_proposal -#: help:purchase.line.proposal,partially_received:0 -#: help:purchase.order,partially_received:0 -msgid "Checked if at least partially delivered" -msgstr "Coché si la ligne est partiellement livré" - #. module: purchase_update_proposal #: help:purchase.order,proposal_updatable:0 msgid "" @@ -248,12 +242,6 @@ msgstr "Mois de commande" msgid "Partially" msgstr "Partially" -#. module: purchase_update_proposal -#: field:purchase.line.proposal,partially_received:0 -#: field:purchase.order,partially_received:0 -msgid "Partially received" -msgstr "Partially received" - #. module: purchase_update_proposal #: model:ir.model,name:purchase_update_proposal.model_res_partner msgid "Partner" diff --git a/purchase_update_proposal/i18n/purchase_update_proposal.pot b/purchase_update_proposal/i18n/purchase_update_proposal.pot index e9e7c56cf84..5230e601b39 100644 --- a/purchase_update_proposal/i18n/purchase_update_proposal.pot +++ b/purchase_update_proposal/i18n/purchase_update_proposal.pot @@ -57,12 +57,6 @@ msgstr "" msgid "Check price on proposal" msgstr "" -#. module: purchase_update_proposal -#: help:purchase.line.proposal,partially_received:0 -#: help:purchase.order,partially_received:0 -msgid "Checked if at least partially delivered" -msgstr "" - #. module: purchase_update_proposal #: help:purchase.order,proposal_updatable:0 msgid "Computed to be sure that stock move are in a state compatible with update" @@ -238,12 +232,6 @@ msgstr "" msgid "Partially" msgstr "" -#. module: purchase_update_proposal -#: field:purchase.line.proposal,partially_received:0 -#: field:purchase.order,partially_received:0 -msgid "Partially received" -msgstr "" - #. module: purchase_update_proposal #: model:ir.model,name:purchase_update_proposal.model_res_partner msgid "Partner" diff --git a/purchase_update_proposal/models/purchase_order.py b/purchase_update_proposal/models/purchase_order.py index d5cbbd55c4b..b2af9c200d5 100644 --- a/purchase_update_proposal/models/purchase_order.py +++ b/purchase_update_proposal/models/purchase_order.py @@ -28,9 +28,8 @@ class PurchaseOrder(models.Model): copy=False, default="draft", ) - proposal_updatable = fields.Selection( - selection=[("", "undefined"), ("yes", "Yes"), ("no", "No")], - default="", + proposal_updatable = fields.Boolean( + default=True, copy=False, help="Computed to be sure that stock move are in a state " "compatible with update", @@ -47,38 +46,29 @@ class PurchaseOrder(models.Model): string="Display/Hide Proposal", help="If checked, rejected proposals are hidden.", ) - partially_received = fields.Boolean( - compute="_compute_partially_received", - store=False, - compute_sudo=True, - help="Checked if at least partially delivered", - ) - - def _compute_partially_received(self): - for rec in self: - received = [x.received for x in rec.order_line if x.received] - if received: - rec.partially_received = True - else: - rec.partially_received = False def _check_updatable_proposal(self): """Override original method""" for rec in self: prevent_update = False - if rec.partially_received: - for __, vals in rec._prepare_proposal_data().items(): - for elm in vals: - if "product_qty" in elm: + if rec.receipt_status == "full": + rec.proposal_updatable = False + elif rec.receipt_status in (False, "pending"): + rec.proposal_updatable = True + elif rec.receipt_status == "partial": + for pol, vals_list in rec._prepare_proposal_data().items(): + for vals in vals_list: + if ( + "product_qty" in vals + and pol + and pol.qty_received > vals["product_qty"] + ): prevent_update = prevent_update or True else: prevent_update = prevent_update or False - if prevent_update: - rec.proposal_updatable = "no" - else: - rec.proposal_updatable = "yes" + rec.proposal_updatable = not prevent_update else: - rec.proposal_updatable = "yes" + rec.proposal_updatable = True def _compute_proposal(self): for rec in self: @@ -97,7 +87,7 @@ def submit_proposal(self): self.ensure_one() self._prepare_proposal_data() self._check_updatable_proposal() - if self.proposal_updatable == "yes": + if self.proposal_updatable: self.write({"proposal_state": "submitted"}) def reset_proposal(self): @@ -120,63 +110,24 @@ def approve_proposal(self): if not self._get_purchase_groups(): raise UserError(_("You are not authorized to approve this proposal")) self._check_updatable_proposal() - if self.proposal_updatable == "no": - # example: qty is in proposal and purchase is partially received + if not self.proposal_updatable: + # example: qty is in proposal and purchase unshipped is lower return body = [] - initial_state = False # these proposals'll reset these lines lines_to_0 = self.proposal_ids.filtered(lambda s: s.qty == 0.0).mapped( "line_id" ) data = self._prepare_proposal_data() if data: - self._hook_pending_proposal_approval() - if self._product_qty_key_in_data(data) and self.state in [ - "confirmed", - "approved", - ]: - # We have to reset order because of changed qty in confirmed order - initial_state = self.state - self.action_cancel() - self.action_cancel_draft() self._update_proposal_to_purchase_line(data, body) self.message_post(body="\n".join(body)) - if initial_state in ("approved", "confirmed"): - # altered quantity implied to reset order, then we approve them again - self.signal_workflow("purchase_confirm") - self.signal_workflow("purchase_approve") - # Cancellation cases if lines_to_0: lines_to_0.cancel_from_proposal() # clean accepted proposals self.env["purchase.line.proposal"].search([("order_id", "=", self.id)]).unlink() self.write({"proposal_state": "approved"}) - def _product_qty_key_in_data(self, data): - """Check if 'product_qty' key is anywhere in data""" - self.ensure_one() - qty2update = False - for __, vals in data.items(): - for key in vals: - if "product_qty" in key: - qty2update = qty2update or True - else: - qty2update = qty2update or False - return qty2update - - def _hook_for_cancel_process(self): - "TODO remove: Kept for compatibility" - self._hook_pending_proposal_approval() - - def _hook_pending_proposal_approval(self): - """Cancellation here is a fake one, in fact it's a workaround - to cleanly update purchase when picking has been created. - Context can't be used because it disappears in the global odoo process - So you may make changes in your custom process to capture falsy cancellation - """ - return - def _prepare_proposal_data(self): self.ensure_one() res = defaultdict(list) @@ -247,7 +198,7 @@ def write(self, vals): and self.proposal_state != "rejected" ): vals["proposal_display"] = False - return super(PurchaseOrder, self).write(vals) + return super().write(vals) def _get_purchase_groups(self): """Guess if the user is a standard purchaser""" @@ -259,11 +210,13 @@ def _get_purchase_groups(self): ] def _fields_prevent_to_update(self, vals): + "Unauthorised users should only update a strict subset of purchase fields" if [x for x in vals.keys() if x[:9] != "proposal_"]: return True return False def _subscribe_portal_vendor(self): + "You may call it to submit this document to partners" self.ensure_one() cial_partner = self.partner_id.commercial_partner_id partner_ids = cial_partner.child_ids.ids @@ -277,7 +230,7 @@ def _subscribe_portal_vendor(self): in s.groups_id ) if users: - self.message_subscribe_users(users.ids) + self.message_subscribe(users.mapped("partner_id").ids) def button_switch2other_view(self): self.ensure_one() diff --git a/purchase_update_proposal/models/purchase_order_line.py b/purchase_update_proposal/models/purchase_order_line.py index b6b6d279693..b0e9bc04b98 100644 --- a/purchase_update_proposal/models/purchase_order_line.py +++ b/purchase_update_proposal/models/purchase_order_line.py @@ -18,33 +18,11 @@ class PurchaseOrderLine(models.Model): compute="_compute_supplier_cancel_status", help="Indicate if the line is cancelled", ) - received = fields.Selection( - selection=[("", "No"), ("partially", "Partially"), ("all", "All")], - compute="_compute_received", - compute_sudo=True, - store=False, - help="Defined if quantity is partially or " - "completely received (at least the quantity of the line)", - ) - - def _compute_received(self): - for rec in self: - received = sum( - rec.move_ids.filtered(lambda s: s.state == "done").mapped( - "product_uom_qty" - ) - ) - if received >= rec.product_qty: - rec.received = "all" - elif not received: - rec.received = "" - else: - rec.received = "partially" def _compute_supplier_cancel_status(self): for rec in self: cancel_status = False - if rec.state == "cancel": + if rec.state == "cancel" or not rec.product_qty: cancel_status = _("Cancel") rec.supplier_cancel_status = cancel_status diff --git a/purchase_update_proposal/models/purchase_proposal.py b/purchase_update_proposal/models/purchase_proposal.py index 36011fd4c92..78f15e2ac8b 100644 --- a/purchase_update_proposal/models/purchase_proposal.py +++ b/purchase_update_proposal/models/purchase_proposal.py @@ -39,11 +39,8 @@ class PurchaseLineProposal(models.Model): date = fields.Date( string="New Date", compute="_compute_date", store=True, readonly=False ) - price_u = fields.Float( - string="New Price U.", - digits="Product Price", - ) - partially_received = fields.Boolean(related="order_id.partially_received") + price_u = fields.Float(string="New Price U.", digits="Product Price") + receipt_status = fields.Selection(related="order_id.receipt_status") check_price = fields.Boolean(related="order_id.partner_id.check_price_on_proposal") @api.depends("order_id.proposal_date") diff --git a/purchase_update_proposal/static/description/index.html b/purchase_update_proposal/static/description/index.html index b13b35a9b10..3c9b5e7c2c2 100644 --- a/purchase_update_proposal/static/description/index.html +++ b/purchase_update_proposal/static/description/index.html @@ -1,20 +1,19 @@ - - + Purchase Update Proposal