[IMP] nfu_sale_order_batch_packaging: add Open Products tab
This commit is contained in:
parent
f513c0919f
commit
a4c9a57ef5
10 changed files with 107 additions and 6 deletions
|
|
@ -25,6 +25,8 @@ NFU Sale Order Batch Packaging
|
||||||
This module adds a minium and a maximum order quantity field to Sale Orders
|
This module adds a minium and a maximum order quantity field to Sale Orders
|
||||||
and adds a packaging default to sale order batch.
|
and adds a packaging default to sale order batch.
|
||||||
|
|
||||||
|
It adds an autosplitting methode that respects the maximum ordered qty for every order and spreads the open packeges evenly over all orders.
|
||||||
|
|
||||||
**Table of contents**
|
**Table of contents**
|
||||||
|
|
||||||
.. contents::
|
.. contents::
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,11 @@ msgstr "Anzeigename"
|
||||||
msgid "Fill Packages"
|
msgid "Fill Packages"
|
||||||
msgstr "Pakete befüllen"
|
msgstr "Pakete befüllen"
|
||||||
|
|
||||||
|
#. module: nfu_sale_order_batch_packaging
|
||||||
|
#: model_terms:ir.ui.view,arch_db:nfu_sale_order_batch_packaging.view_order_batch_form
|
||||||
|
msgid "Open Products"
|
||||||
|
msgstr "Offene Produkte"
|
||||||
|
|
||||||
#. module: nfu_sale_order_batch_packaging
|
#. module: nfu_sale_order_batch_packaging
|
||||||
#: model:ir.model.fields.selection,name:nfu_sale_order_batch_packaging.selection__sale_order_batch_product__open_packaging_state__full
|
#: model:ir.model.fields.selection,name:nfu_sale_order_batch_packaging.selection__sale_order_batch_product__open_packaging_state__full
|
||||||
msgid "Full"
|
msgid "Full"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
from odoo import api, fields, models
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
from odoo.addons.sale_order_batch.models.sale_order_batch import READONLY_FIELD_STATES
|
||||||
|
|
||||||
|
|
||||||
class SaleOrderBatch(models.Model):
|
class SaleOrderBatch(models.Model):
|
||||||
_inherit = "sale.order.batch"
|
_inherit = "sale.order.batch"
|
||||||
|
|
@ -10,6 +12,12 @@ class SaleOrderBatch(models.Model):
|
||||||
has_fillable_packages = fields.Boolean(
|
has_fillable_packages = fields.Boolean(
|
||||||
compute="_compute_has_fillable_packages",
|
compute="_compute_has_fillable_packages",
|
||||||
)
|
)
|
||||||
|
open_product_ids = fields.Many2many(
|
||||||
|
comodel_name="sale.order.batch.product",
|
||||||
|
compute="_compute_open_product_ids",
|
||||||
|
inverse="_inverse_open_product_ids",
|
||||||
|
states=READONLY_FIELD_STATES,
|
||||||
|
)
|
||||||
|
|
||||||
@api.depends(
|
@api.depends(
|
||||||
"sale_order_line_ids.product_uom_qty",
|
"sale_order_line_ids.product_uom_qty",
|
||||||
|
|
@ -29,6 +37,16 @@ class SaleOrderBatch(models.Model):
|
||||||
p.open_packaging_qty > 0 for p in batch.product_ids
|
p.open_packaging_qty > 0 for p in batch.product_ids
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@api.depends("product_ids.open_packaging_qty")
|
||||||
|
def _compute_open_product_ids(self):
|
||||||
|
for batch in self:
|
||||||
|
batch.open_product_ids = batch.product_ids.filtered(
|
||||||
|
lambda p: p.open_packaging_qty > 0
|
||||||
|
)
|
||||||
|
|
||||||
|
def _inverse_open_product_ids(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def action_restore_ordered_qty(self):
|
def action_restore_ordered_qty(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
lines = self.sale_order_line_ids.filtered(
|
lines = self.sale_order_line_ids.filtered(
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,32 @@ class SaleOrderBatchProduct(models.Model):
|
||||||
for line in product.sale_order_line_ids
|
for line in product.sale_order_line_ids
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def action_fill_packages(self):
|
||||||
|
self.ensure_one()
|
||||||
|
if self.open_packaging_qty > 0 and self.open_packaging_max_qty == 0.0:
|
||||||
|
line_ids = [(0, 0, {"batch_product_id": self.id})]
|
||||||
|
zero_line_ids = []
|
||||||
|
elif self.open_packaging_qty > 0 and self.open_packaging_max_qty != 0.0:
|
||||||
|
line_ids = []
|
||||||
|
zero_line_ids = [(0, 0, {"batch_product_id": self.id})]
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
wizard = self.env["sale.order.batch.packaging.fill"].create(
|
||||||
|
{
|
||||||
|
"batch_id": self.batch_id.id,
|
||||||
|
"line_ids": line_ids,
|
||||||
|
"zero_line_ids": zero_line_ids,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"type": "ir.actions.act_window",
|
||||||
|
"name": "Fill Open Packages",
|
||||||
|
"res_model": "sale.order.batch.packaging.fill",
|
||||||
|
"res_id": wizard.id,
|
||||||
|
"view_mode": "form",
|
||||||
|
"target": "new",
|
||||||
|
}
|
||||||
|
|
||||||
def action_restore_ordered_qty(self):
|
def action_restore_ordered_qty(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
lines = self.sale_order_line_ids.filtered(
|
lines = self.sale_order_line_ids.filtered(
|
||||||
|
|
@ -128,7 +154,11 @@ class SaleOrderBatchProduct(models.Model):
|
||||||
fair_share = float_round(remaining / n, precision_rounding=rounding)
|
fair_share = float_round(remaining / n, precision_rounding=rounding)
|
||||||
addition = min(room, fair_share)
|
addition = min(room, fair_share)
|
||||||
if addition > 0:
|
if addition > 0:
|
||||||
line.write({"product_uom_qty": line.product_uom_qty + addition})
|
new_qty = float_round(
|
||||||
|
line.product_uom_qty + addition, precision_rounding=rounding
|
||||||
|
)
|
||||||
|
new_qty = min(new_qty, line.product_uom_max_qty)
|
||||||
|
line.write({"product_uom_qty": new_qty})
|
||||||
distributed += addition
|
distributed += addition
|
||||||
last_line = line
|
last_line = line
|
||||||
n -= 1
|
n -= 1
|
||||||
|
|
@ -137,7 +167,11 @@ class SaleOrderBatchProduct(models.Model):
|
||||||
# Attach any sub-rounding remainder directly to the last eligible line
|
# Attach any sub-rounding remainder directly to the last eligible line
|
||||||
remainder = target - distributed
|
remainder = target - distributed
|
||||||
if remainder > 1e-9 and last_line:
|
if remainder > 1e-9 and last_line:
|
||||||
last_line.write({"product_uom_qty": last_line.product_uom_qty + remainder})
|
new_qty = float_round(
|
||||||
|
last_line.product_uom_qty + remainder, precision_rounding=rounding
|
||||||
|
)
|
||||||
|
new_qty = min(new_qty, last_line.product_uom_max_qty)
|
||||||
|
last_line.write({"product_uom_qty": new_qty})
|
||||||
|
|
||||||
def _zero_packaging(self):
|
def _zero_packaging(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from odoo import _, api, fields, models
|
from odoo import _, api, fields, models
|
||||||
from odoo.exceptions import UserError
|
from odoo.exceptions import UserError
|
||||||
|
from odoo.tools import float_round
|
||||||
|
|
||||||
|
|
||||||
class SaleOrderLine(models.Model):
|
class SaleOrderLine(models.Model):
|
||||||
|
|
@ -21,7 +22,13 @@ class SaleOrderLine(models.Model):
|
||||||
@api.constrains("product_uom_qty", "product_uom_max_qty")
|
@api.constrains("product_uom_qty", "product_uom_max_qty")
|
||||||
def _check_product_uom_qty(self):
|
def _check_product_uom_qty(self):
|
||||||
for order_line in self:
|
for order_line in self:
|
||||||
if order_line.product_uom_qty > order_line.product_uom_max_qty:
|
if (
|
||||||
|
float_round(
|
||||||
|
order_line.product_uom_max_qty - order_line.product_uom_qty,
|
||||||
|
precision_rounding=order_line.product_uom.rounding,
|
||||||
|
)
|
||||||
|
< 0
|
||||||
|
):
|
||||||
raise UserError(
|
raise UserError(
|
||||||
_(
|
_(
|
||||||
f"{order_line.order_id.name}, {order_line.product_id.name}: "
|
f"{order_line.order_id.name}, {order_line.product_id.name}: "
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,4 @@
|
||||||
This module adds a minium and a maximum order quantity field to Sale Orders
|
This module adds a minium and a maximum order quantity field to Sale Orders
|
||||||
and adds a packaging default to sale order batch.
|
and adds a packaging default to sale order batch.
|
||||||
|
|
||||||
|
It adds an autosplitting methode that respects the maximum ordered qty for every order and spreads the open packeges evenly over all orders.
|
||||||
|
|
@ -372,6 +372,7 @@ ul.auto-toc {
|
||||||
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/licence-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/bakeupboys/nfu/tree/16.0/nfu_sale_order_batch_packaging"><img alt="bakeupboys/nfu" src="https://img.shields.io/badge/github-bakeupboys%2Fnfu-lightgray.png?logo=github" /></a></p>
|
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/licence-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/bakeupboys/nfu/tree/16.0/nfu_sale_order_batch_packaging"><img alt="bakeupboys/nfu" src="https://img.shields.io/badge/github-bakeupboys%2Fnfu-lightgray.png?logo=github" /></a></p>
|
||||||
<p>This module adds a minium and a maximum order quantity field to Sale Orders
|
<p>This module adds a minium and a maximum order quantity field to Sale Orders
|
||||||
and adds a packaging default to sale order batch.</p>
|
and adds a packaging default to sale order batch.</p>
|
||||||
|
<p>It adds an autosplitting methode that respects the maximum ordered qty for every order and spreads the open packeges evenly over all orders.</p>
|
||||||
<p><strong>Table of contents</strong></p>
|
<p><strong>Table of contents</strong></p>
|
||||||
<div class="contents local topic" id="contents">
|
<div class="contents local topic" id="contents">
|
||||||
<ul class="simple">
|
<ul class="simple">
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,14 @@
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//header" position="inside">
|
<xpath expr="//header" position="inside">
|
||||||
<field name="has_qty_adjusted" invisible="1"/>
|
<field name="has_qty_adjusted" invisible="1"/>
|
||||||
|
<field name="open_packaging_qty" invisible="1"/>
|
||||||
|
<button
|
||||||
|
name="action_fill_packages"
|
||||||
|
type="object"
|
||||||
|
string="Fill Packages"
|
||||||
|
class="btn-primary"
|
||||||
|
attrs="{'invisible': [('open_packaging_qty', '=', 0.0)]}"
|
||||||
|
/>
|
||||||
<button
|
<button
|
||||||
name="action_restore_ordered_qty"
|
name="action_restore_ordered_qty"
|
||||||
type="object"
|
type="object"
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,30 @@
|
||||||
<xpath expr="//field[@name='product_ids']/tree/field[@name='product_id']" position="attributes">
|
<xpath expr="//field[@name='product_ids']/tree/field[@name='product_id']" position="attributes">
|
||||||
<attribute name="decoration-warning">(open_packaging_qty != 0)</attribute>
|
<attribute name="decoration-warning">(open_packaging_qty != 0)</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
<!-- Open Products tab -->
|
||||||
|
<xpath expr="//page[@name='products']" position="after">
|
||||||
|
<page string="Open Products" name="open_products">
|
||||||
|
<field name="open_product_ids">
|
||||||
|
<tree create="0" delete="0">
|
||||||
|
<field name="product_template_id" optional="hide"/>
|
||||||
|
<field name="product_id"/>
|
||||||
|
<field name="product_uom_qty"/>
|
||||||
|
<field name="product_uom_max_qty"/>
|
||||||
|
<field
|
||||||
|
name="open_packaging_qty"
|
||||||
|
decoration-danger="open_packaging_qty != 0"
|
||||||
|
/>
|
||||||
|
<field name="open_packaging_max_qty" optional="hide"/>
|
||||||
|
<field
|
||||||
|
name="open_packaging_state"
|
||||||
|
widget="badge"
|
||||||
|
decoration-warning="open_packaging_state == 'open'"
|
||||||
|
decoration-danger="open_packaging_state == 'last_open'"
|
||||||
|
/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
>
|
>
|
||||||
<field name="has_fill_lines" invisible="1"/>
|
<field name="has_fill_lines" invisible="1"/>
|
||||||
<field name="line_ids">
|
<field name="line_ids">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom" delete="0">
|
||||||
<field name="product_id" readonly="1"/>
|
<field name="product_id" readonly="1"/>
|
||||||
<field name="open_packaging_qty" readonly="1"/>
|
<field name="open_packaging_qty" readonly="1"/>
|
||||||
<field name="batch_fill_precision"/>
|
<field name="batch_fill_precision"/>
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
<page string="Zero Out" name="zero_out" attrs="{'invisible': [('has_zero_lines', '=', False)]}">
|
<page string="Zero Out" name="zero_out" attrs="{'invisible': [('has_zero_lines', '=', False)]}">
|
||||||
<field name="has_zero_lines" invisible="1"/>
|
<field name="has_zero_lines" invisible="1"/>
|
||||||
<field name="zero_line_ids">
|
<field name="zero_line_ids">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom" delete="0">
|
||||||
<field name="product_id" readonly="1"/>
|
<field name="product_id" readonly="1"/>
|
||||||
<field name="open_packaging_qty" readonly="1"/>
|
<field name="open_packaging_qty" readonly="1"/>
|
||||||
<field name="keep_qty"/>
|
<field name="keep_qty"/>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue