-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from Escodoo/14.0-add-egd_sale_blanket_order_cu…
…stom [14.0][ADD] egd_sale_blanket_order_custom: add new module
- Loading branch information
Showing
22 changed files
with
726 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,3 @@ | ||
|
||
exclude: | | ||
(?x) | ||
# NOT INSTALLABLE ADDONS | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
============================= | ||
Egd Sale Blanket Order Custom | ||
============================= | ||
.. |badge1| image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg | ||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html | ||
:alt: License: AGPL-3 | ||
|
||
|badge1| | ||
|
||
EGD Sale Blanket Order Custom | ||
|
||
**Table of contents** | ||
|
||
.. contents:: | ||
:local: | ||
|
||
Configuration | ||
============= | ||
|
||
To Configure... | ||
|
||
Usage | ||
===== | ||
|
||
To usage... | ||
|
||
Bug Tracker | ||
=========== | ||
|
||
Bugs are tracked on `GitHub Issues | ||
<https://github.com/Escodoo/{project_repo}/issues>`_. In case of trouble, please | ||
check there if your issue has already been reported. If you spotted it first, | ||
help us smash it by providing detailed and welcomed feedback. | ||
|
||
Do not contact contributors directly about support or help with technical issues. | ||
|
||
Credits | ||
======= | ||
|
||
Authors | ||
~~~~~~~ | ||
|
||
* Escodoo | ||
|
||
Contributors | ||
~~~~~~~~~~~~ | ||
|
||
* Marcel Savegnago <[email protected]> | ||
|
||
Other credits | ||
~~~~~~~~~~~~~ | ||
|
||
The development of this module has been financially supported by: | ||
|
||
* Escodoo - `https://www.escodoo.com.br <https://www.escodoo.com.br>`_ | ||
|
||
Maintainers | ||
~~~~~~~~~~~ | ||
|
||
This module is maintained by the Escodoo. | ||
|
||
.. |maintainer-escodoo| image:: https://github.com/escodoo.png?size=80px | ||
:target: https://github.com/Escodoo | ||
:alt: escodoo | ||
|
||
|maintainer-escodoo| | ||
|
||
We at Escodoo are exclusively dedicated to deploying the Odoo Platform and are | ||
focused on providing solutions that make our customers more competitive, lowering | ||
costs, making technology more accessible and ensuring it is used strategically to | ||
add even more value to the business. | ||
|
||
.. |maintainer-marcelsavegnago| image:: https://github.com/marcelsavegnago.png?size=40px | ||
:target: https://github.com/marcelsavegnago | ||
:alt: marcelsavegnago | ||
|
||
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__: | ||
|
||
|maintainer-marcelsavegnago| | ||
|
||
To contribute to this module, please visit https://www.escodoo.com.br. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Copyright 2023 - TODAY, Kaynnan Lemes <[email protected]> | ||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). | ||
from . import models | ||
from . import wizard |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# Copyright 2023 - TODAY, Escodoo | ||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). | ||
|
||
{ | ||
"name": "EGD Sale Blanket Order Custom", | ||
"summary": """ | ||
EGD Sale Blanket Order Custom""", | ||
"version": "14.0.1.0.0", | ||
"license": "AGPL-3", | ||
"author": "Escodoo", | ||
"website": "https://github.com/Escodoo/egd-addons", | ||
"depends": ["sale_blanket_order"], | ||
"data": [ | ||
"security/ir.model.access.csv", | ||
"wizard/sale_create_order_plan_view.xml", | ||
"wizard/sale_make_planned_order_view.xml", | ||
"views/sale_view.xml", | ||
], | ||
# 'demo': [], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Copyright 2023 - TODAY, Kaynnan Lemes <[email protected]> | ||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). | ||
from . import blanket_orders | ||
from . import sale_order_plan |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/) | ||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) | ||
from dateutil.relativedelta import relativedelta | ||
|
||
from odoo import _, api, fields, models | ||
from odoo.exceptions import UserError, ValidationError | ||
from odoo.tools.float_utils import float_round | ||
|
||
|
||
class SaleBlanketOrder(models.Model): | ||
_inherit = "sale.blanket.order" | ||
|
||
order_plan_ids = fields.One2many( | ||
comodel_name="sale.order.plan", | ||
inverse_name="sale_id", | ||
string="Order Plan", | ||
copy=False, | ||
) | ||
use_order_plan = fields.Boolean( | ||
string="Use Order Plan", | ||
default=False, | ||
copy=False, | ||
) | ||
ip_order_plan = fields.Boolean( | ||
string="Order Plan In Process", | ||
compute="_compute_ip_order_plan", | ||
help="At least one order plan line pending to create order", | ||
) | ||
|
||
def _compute_ip_order_plan(self): | ||
for rec in self: | ||
has_order_plan = rec.use_order_plan and rec.order_plan_ids | ||
to_order = rec.order_plan_ids.filtered(lambda l: not l.ordered) | ||
if rec.state == "open" and has_order_plan and to_order: | ||
rec.ip_order_plan = True | ||
continue | ||
rec.ip_order_plan = False | ||
|
||
@api.constrains("state") | ||
def _check_order_plan(self): | ||
for rec in self: | ||
if rec.state != "draft": | ||
if rec.order_plan_ids.filtered(lambda l: not l.percent): | ||
raise ValidationError( | ||
_("Please fill percentage for all order plan lines") | ||
) | ||
|
||
def action_confirm(self): | ||
if self.filtered(lambda r: r.use_order_plan and not r.order_plan_ids): | ||
raise UserError(_("Use Order Plan selected, but no plan created")) | ||
return super().action_confirm() | ||
|
||
def create_order_plan( | ||
self, num_installment, installment_date, interval, interval_type | ||
): | ||
self.ensure_one() | ||
self.order_plan_ids.unlink() | ||
order_plans = [] | ||
Decimal = self.env["decimal.precision"] | ||
prec = Decimal.precision_get("Product Unit of Measure") | ||
percent = float_round(1.0 / num_installment * 100, prec) | ||
percent_last = 100 - (percent * (num_installment - 1)) | ||
# Normal | ||
for i in range(num_installment): | ||
this_installment = i + 1 | ||
if num_installment == this_installment: | ||
percent = percent_last | ||
vals = { | ||
"installment": this_installment, | ||
"plan_date": installment_date, | ||
"order_type": "installment", | ||
"percent": percent, | ||
} | ||
order_plans.append((0, 0, vals)) | ||
installment_date = self._next_date( | ||
installment_date, interval, interval_type | ||
) | ||
self.write({"order_plan_ids": order_plans}) | ||
return True | ||
|
||
def remove_order_plan(self): | ||
self.ensure_one() | ||
self.order_plan_ids.unlink() | ||
return True | ||
|
||
@api.model | ||
def _next_date(self, installment_date, interval, interval_type): | ||
installment_date = fields.Date.from_string(installment_date) | ||
if interval_type == "month": | ||
next_date = installment_date + relativedelta(months=+interval) | ||
elif interval_type == "year": | ||
next_date = installment_date + relativedelta(years=+interval) | ||
else: | ||
next_date = installment_date + relativedelta(days=+interval) | ||
next_date = fields.Date.to_string(next_date) | ||
return next_date | ||
|
||
def _create_sale_order(self): | ||
order_plan_id = self._context.get("order_plan_id") | ||
lines = [ | ||
( | ||
0, | ||
0, | ||
{ | ||
"blanket_line_id": line.id, | ||
"product_id": line.product_id.id, | ||
"date_schedule": line.date_schedule, | ||
"remaining_uom_qty": line.remaining_uom_qty, | ||
"price_unit": line.price_unit, | ||
"product_uom": line.product_uom, | ||
"qty": line.remaining_uom_qty, | ||
"partner_id": line.partner_id, | ||
}, | ||
) | ||
for line in self.line_ids | ||
] | ||
|
||
wizard = ( | ||
self.env["sale.blanket.order.wizard"] | ||
.with_context(active_id=self.id, active_model="sale.blanket.order") | ||
.create({"blanket_order_id": self.id, "line_ids": lines}) | ||
) | ||
|
||
result = wizard.create_sale_order() # Create Sale Order using Wizard | ||
sale_order_id = result.get("domain", [])[0][2][0] # Get ID in domain | ||
orders = self.env["sale.order"].search( | ||
[("id", "=", sale_order_id)] | ||
) # Easy locate for sale.order | ||
blanket_orders = self.env["sale.blanket.order"].browse( | ||
self.id | ||
) # Usage for compute new quantity | ||
if order_plan_id: | ||
plan = self.env["sale.order.plan"].browse(order_plan_id) | ||
for order in orders: | ||
plan._compute_new_order_quantity(blanket_orders) | ||
order.date_order = plan.plan_date | ||
plan.sale_order_ids += orders | ||
return orders |
122 changes: 122 additions & 0 deletions
122
egd_sale_blanket_order_custom/models/sale_order_plan.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
from odoo import _, fields, models | ||
from odoo.exceptions import UserError, ValidationError | ||
from odoo.tools.float_utils import float_compare, float_round | ||
|
||
|
||
class SaleOrderPlan(models.Model): | ||
_name = "sale.order.plan" | ||
_description = "Order Planning Detail" | ||
_order = "installment" | ||
|
||
sale_id = fields.Many2one( | ||
comodel_name="sale.blanket.order", | ||
string="Sales Order", | ||
index=True, | ||
readonly=True, | ||
ondelete="cascade", | ||
) | ||
partner_id = fields.Many2one( | ||
comodel_name="res.partner", | ||
string="Customer", | ||
related="sale_id.partner_id", | ||
store=True, | ||
index=True, | ||
) | ||
state = fields.Selection( | ||
string="Status", | ||
related="sale_id.state", | ||
store=True, | ||
index=True, | ||
) | ||
installment = fields.Integer(string="Installment") | ||
plan_date = fields.Date(string="Plan Date", required=True) | ||
order_type = fields.Selection( | ||
[("installment", "Installment")], | ||
string="Type", | ||
required=True, | ||
default="installment", | ||
) | ||
last = fields.Boolean( | ||
string="Last Installment", | ||
compute="_compute_last", | ||
help="Last installment will create order use remaining amount", | ||
) | ||
percent = fields.Float( | ||
string="Percent", | ||
digits="Product Unit of Measure", | ||
help="This percent will be used to calculate new quantity", | ||
) | ||
sale_order_ids = fields.Many2many( | ||
"sale.order", | ||
relation="sale_order_plan_order_rel", | ||
column1="plan_id", | ||
column2="order_id", | ||
string="Orders", | ||
readonly=True, | ||
) | ||
to_order = fields.Boolean( | ||
string="Next Order", | ||
compute="_compute_to_order", | ||
help="If this line is ready to create new order", | ||
) | ||
ordered = fields.Boolean( | ||
string="Order Created", | ||
compute="_compute_ordered", | ||
help="If this line already ordered", | ||
) | ||
_sql_constraint = [ | ||
( | ||
"unique_instalment", | ||
"UNIQUE (sale_id, installment)", | ||
"Installment must be unique on order plan", | ||
) | ||
] | ||
|
||
def _compute_to_order(self): | ||
"""If any order is in draft/done do not allow to create sale order. | ||
Only if previous to_order is False, it is eligible to_order. | ||
""" | ||
for rec in self: | ||
rec.to_order = False | ||
for rec in self.sorted("installment"): | ||
if rec.state != "open": # Not confirmed, no to_order | ||
continue | ||
if not rec.ordered: | ||
rec.to_order = True | ||
break | ||
|
||
def _compute_ordered(self): | ||
for rec in self: | ||
ordered = rec.sale_order_ids.filtered( | ||
lambda l: l.state in ("draft", "sale") | ||
) | ||
rec.ordered = ordered and True or False | ||
|
||
def _compute_last(self): | ||
for rec in self: | ||
last = max(rec.sale_id.order_plan_ids.mapped("installment")) | ||
rec.last = rec.installment == last | ||
|
||
def _compute_new_order_quantity(self, blanket_order): | ||
self.ensure_one() | ||
if self.last: # For last install, let the system do the calc. | ||
return | ||
percent = self.percent | ||
for blanket_line in blanket_order.line_ids: | ||
first_blanket = fields.first(blanket_line) | ||
plan_qty = first_blanket.original_uom_qty * (percent / 100) | ||
prec = first_blanket.product_uom.rounding | ||
for order_line in blanket_line.sale_lines: | ||
if not len(order_line): | ||
raise UserError(_("No matched order line for sale order")) | ||
if plan_qty: | ||
plan_qty = float_round(plan_qty, precision_rounding=prec) | ||
if float_compare(plan_qty, order_line.product_uom_qty, prec) == 1: | ||
raise ValidationError( | ||
_( | ||
"Plan quantity: %s, exceeds orderable quantity: %s" | ||
"\nProduct should be available before creating the order" | ||
) | ||
% (plan_qty, order_line.product_uom_qty) | ||
) | ||
order_line.write({"product_uom_qty": plan_qty}) |
Empty file.
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink | ||
access_sale_order_plan,access_sale_order_plan,model_sale_order_plan,,1,1,1,1 | ||
access_sale_create_order_plan,access_sale_create_order_plan,model_sale_create_order_plan,,1,1,1,1 | ||
access_sale_make_planned_order,access_sale_make_planned_order,model_sale_make_planned_order,,1,1,1,1 |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.