Skip to content

Commit

Permalink
Merge pull request #1 from Escodoo/14.0-add-egd_sale_blanket_order_cu…
Browse files Browse the repository at this point in the history
…stom

[14.0][ADD] egd_sale_blanket_order_custom: add new module
  • Loading branch information
marcelsavegnago authored Sep 11, 2023
2 parents 40ff1bd + e276739 commit 21e2df5
Show file tree
Hide file tree
Showing 22 changed files with 726 additions and 1 deletion.
1 change: 0 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

exclude: |
(?x)
# NOT INSTALLABLE ADDONS
Expand Down
81 changes: 81 additions & 0 deletions egd_sale_blanket_order_custom/README.rst
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.
4 changes: 4 additions & 0 deletions egd_sale_blanket_order_custom/__init__.py
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
20 changes: 20 additions & 0 deletions egd_sale_blanket_order_custom/__manifest__.py
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': [],
}
4 changes: 4 additions & 0 deletions egd_sale_blanket_order_custom/models/__init__.py
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
138 changes: 138 additions & 0 deletions egd_sale_blanket_order_custom/models/blanket_orders.py
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 egd_sale_blanket_order_custom/models/sale_order_plan.py
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.
4 changes: 4 additions & 0 deletions egd_sale_blanket_order_custom/security/ir.model.access.csv
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.
Loading

0 comments on commit 21e2df5

Please sign in to comment.