Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit fd8b3a2

Browse files
committedMar 3, 2025·
add stock_picking_declared_value
1 parent ae19d89 commit fd8b3a2

17 files changed

+860
-0
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import setuptools
2+
3+
setuptools.setup(
4+
setup_requires=['setuptools-odoo'],
5+
odoo_addon=True,
6+
)
+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
============================
2+
Stock Picking Declared Value
3+
============================
4+
5+
..
6+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7+
!! This file is generated by oca-gen-addon-readme !!
8+
!! changes will be overwritten. !!
9+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10+
11+
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
12+
:target: https://odoo-community.org/page/development-status
13+
:alt: Beta
14+
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
15+
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
16+
:alt: License: AGPL-3
17+
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fdelivery--carrier-lightgray.png?logo=github
18+
:target: https://github.com/OCA/delivery-carrier/tree/15.0/stock_picking_declared_value
19+
:alt: OCA/delivery-carrier
20+
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
21+
:target: https://translation.odoo-community.org/projects/delivery-carrier-15-0/delivery-carrier-15-0-stock_picking_declared_value
22+
:alt: Translate me on Weblate
23+
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
24+
:target: https://runboat.odoo-community.org/builds?repo=OCA/delivery-carrier&target_branch=15.0
25+
:alt: Try me on Runboat
26+
27+
|badge1| |badge2| |badge3| |badge4| |badge5|
28+
29+
This module extends the stock picking functionality to display product declared values from sales orders.
30+
When a picking is created from a sales order, it copies the prices and discounts of each product line to the picking as declared values.
31+
The purpose of this module is to serve other modules such as delivery carriers which require these
32+
declared values when shipping a picking.
33+
34+
The module copies the following information from the sales order lines:
35+
- Unit price
36+
- Discount percentage
37+
- Calculates the subtotal with discount applied
38+
39+
Additionally, the module allows configuring a "Declared Amount" on shipping methods (delivery carriers).
40+
This percentage is used to calculate the final declared value for the picking. For example, if the total value
41+
of products in a picking is $1000 and the declared amount is set to 80%, the declared value will be $800.
42+
43+
All declared value columns are hidden by default in the user interface but can be shown when needed.
44+
45+
**Table of contents**
46+
47+
.. contents::
48+
:local:
49+
50+
Usage
51+
=====
52+
53+
To use this module, you need to:
54+
55+
#. Configure the "Declared Amount" on your shipping methods (Inventory > Configuration > Delivery > Shipping Methods)
56+
#. Create a sales order with products and select a shipping method
57+
#. Confirm the sales order
58+
#. Go to the delivery order created from the sales order
59+
#. The declared value columns are hidden by default
60+
#. You can show the declared value columns by clicking on the column header and selecting "Show"
61+
#. You can view and modify the "Declared Amount" in the "Additional Info" tab of the picking
62+
63+
Bug Tracker
64+
===========
65+
66+
Bugs are tracked on `GitHub Issues <https://github.com/OCA/delivery-carrier/issues>`_.
67+
In case of trouble, please check there if your issue has already been reported.
68+
If you spotted it first, help us to smash it by providing a detailed and welcomed
69+
`feedback <https://github.com/OCA/delivery-carrier/issues/new?body=module:%20stock_picking_declared_value%0Aversion:%2015.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
70+
71+
Do not contact contributors directly about support or help with technical issues.
72+
73+
Credits
74+
=======
75+
76+
Authors
77+
~~~~~~~
78+
79+
* Nitrokey GmbH
80+
81+
Contributors
82+
~~~~~~~~~~~~
83+
84+
* Nitrokey GmbH
85+
86+
Maintainers
87+
~~~~~~~~~~~
88+
89+
This module is maintained by the OCA.
90+
91+
.. image:: https://odoo-community.org/logo.png
92+
:alt: Odoo Community Association
93+
:target: https://odoo-community.org
94+
95+
OCA, or the Odoo Community Association, is a nonprofit organization whose
96+
mission is to support the collaborative development of Odoo features and
97+
promote its widespread use.
98+
99+
This module is part of the `OCA/delivery-carrier <https://github.com/OCA/delivery-carrier/tree/15.0/stock_picking_declared_value>`_ project on GitHub.
100+
101+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import models
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright 2025 Nitrokey GmbH
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
{
4+
"name": "Stock Picking Declared Value",
5+
"summary": "Display product declared values on stock pickings from sales orders",
6+
"version": "15.0.1.0.0",
7+
"category": "Warehouse",
8+
"website": "https://github.com/OCA/delivery-carrier",
9+
"author": "Nitrokey GmbH, Odoo Community Association (OCA)",
10+
"license": "AGPL-3",
11+
"application": False,
12+
"installable": True,
13+
"depends": [
14+
"stock",
15+
"sale_stock",
16+
"delivery",
17+
],
18+
"data": [
19+
"views/stock_picking_views.xml",
20+
"views/stock_move_views.xml",
21+
"views/delivery_carrier_views.xml",
22+
],
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from . import stock_picking
2+
from . import stock_move
3+
from . import delivery_carrier
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright 2025 Nitrokey GmbH
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
4+
from odoo import fields, models
5+
6+
7+
class DeliveryCarrier(models.Model):
8+
_inherit = "delivery.carrier"
9+
10+
declared_amount = fields.Float(
11+
string="Declared Amount (%)",
12+
default=100.0,
13+
help="Percentage of the sale price to be used as the declared value for shipping. "
14+
"100% means the full sale price will be used.",
15+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Copyright 2025 Nitrokey GmbH
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
4+
from odoo import api, fields, models
5+
6+
7+
class StockMove(models.Model):
8+
_inherit = "stock.move"
9+
10+
sale_unit_price = fields.Float(
11+
string="Unit Price",
12+
digits="Product Price",
13+
compute="_compute_sale_unit_price",
14+
store=True,
15+
help="Unit price from the sale order line",
16+
)
17+
discount = fields.Float(
18+
string="Discount (%)",
19+
digits="Discount",
20+
compute="_compute_sale_unit_price",
21+
store=True,
22+
help="Discount from the sale order line",
23+
)
24+
price_subtotal = fields.Monetary(
25+
string="Subtotal",
26+
compute="_compute_price_subtotal",
27+
store=True,
28+
help="Subtotal amount for the move line",
29+
)
30+
currency_id = fields.Many2one(
31+
related="picking_id.currency_id",
32+
string="Currency",
33+
readonly=True,
34+
)
35+
36+
@api.depends("sale_line_id.price_unit", "sale_line_id.discount")
37+
def _compute_sale_unit_price(self):
38+
"""Copy the price and discount from the sale order line"""
39+
for move in self:
40+
if move.sale_line_id:
41+
move.sale_unit_price = move.sale_line_id.price_unit
42+
move.discount = move.sale_line_id.discount
43+
else:
44+
move.sale_unit_price = move.product_id.lst_price
45+
move.discount = 0.0
46+
47+
@api.depends("sale_unit_price", "product_uom_qty", "discount")
48+
def _compute_price_subtotal(self):
49+
"""Compute the subtotal amount"""
50+
for move in self:
51+
price = move.sale_unit_price * (1 - (move.discount or 0.0) / 100.0)
52+
move.price_subtotal = price * move.product_uom_qty
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Copyright 2025 Nitrokey GmbH
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
4+
from odoo import api, fields, models
5+
6+
7+
class StockPicking(models.Model):
8+
_inherit = "stock.picking"
9+
10+
# Add a computed field to show the total declared value of the picking
11+
amount_total = fields.Monetary(
12+
string="Total Declared Value",
13+
compute="_compute_amount_total",
14+
store=True,
15+
help="Total declared value of the picking based on the values of the products",
16+
)
17+
currency_id = fields.Many2one(
18+
"res.currency",
19+
string="Currency",
20+
related="sale_id.currency_id",
21+
readonly=True,
22+
)
23+
declared_amount = fields.Float(
24+
string="Declared Amount (%)",
25+
default=100.0,
26+
help="Percentage of the sale price to be used as the declared value for shipping. "
27+
"100% means the full sale price will be used.",
28+
)
29+
30+
@api.onchange("carrier_id")
31+
def _onchange_carrier_id(self):
32+
"""Update declared_amount when carrier changes"""
33+
if self.carrier_id and self.carrier_id.declared_amount:
34+
self.declared_amount = self.carrier_id.declared_amount
35+
36+
@api.model
37+
def create(self, vals):
38+
"""Set declared_amount from carrier on creation"""
39+
picking = super().create(vals)
40+
if picking.carrier_id and picking.carrier_id.declared_amount:
41+
picking.declared_amount = picking.carrier_id.declared_amount
42+
return picking
43+
44+
@api.depends("move_lines.price_subtotal", "declared_amount")
45+
def _compute_amount_total(self):
46+
"""Compute the total declared value of the picking"""
47+
for picking in self:
48+
subtotal = sum(picking.move_lines.mapped("price_subtotal"))
49+
picking.amount_total = subtotal * (picking.declared_amount / 100.0)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* Nitrokey GmbH
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
This module extends the stock picking functionality to copy product prices from sales orders.
2+
When a picking is created from a sales order, it copies the prices and discounts of each product line to the picking as declared values.
3+
The purpose of this module is to serve other modules such as delivery carriers which require these
4+
declared values when shipping a picking.
5+
6+
Additionally, the module allows configuring a "Declared Amount" on shipping methods (delivery carriers).
7+
This percentage is used to calculate the final declared value for the picking. For example, if the total value
8+
of products in a picking is $1000 and the declared amount is set to 80%, the declared value will be $800.
9+
10+
All declared value columns are hidden by default in the user interface but can be shown when needed.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
To use this module, you need to:
2+
3+
#. Configure the "Declared Amount" on your shipping methods (Inventory > Configuration > Delivery > Shipping Methods)
4+
#. Create a sales order with products and select a shipping method
5+
#. Confirm the sales order
6+
#. Go to the delivery order created from the sales order
7+
#. The declared value columns are hidden by default
8+
#. You can show the declared value columns by clicking on the column header and selecting "Show"
9+
#. You can view and modify the "Declared Amount" in the "Additional Info" tab of the picking

‎stock_picking_declared_value/static/description/index.html

+443
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import test_stock_picking_declared_value
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Copyright 2025 Nitrokey GmbH
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
4+
from odoo.tests.common import TransactionCase
5+
6+
7+
class TestStockPickingDeclaredValue(TransactionCase):
8+
def setUp(self):
9+
super().setUp()
10+
# Create a product
11+
self.product = self.env["product.product"].create(
12+
{
13+
"name": "Test Product",
14+
"type": "product",
15+
"list_price": 100.0,
16+
}
17+
)
18+
# Create a customer
19+
self.partner = self.env["res.partner"].create(
20+
{
21+
"name": "Test Customer",
22+
"customer_rank": 1,
23+
}
24+
)
25+
# Create a delivery carrier with declared amount percentage
26+
self.carrier = self.env["delivery.carrier"].create(
27+
{
28+
"name": "Test Carrier",
29+
"delivery_type": "fixed",
30+
"product_id": self.product.id,
31+
"fixed_price": 10.0,
32+
"declared_amount": 80.0,
33+
}
34+
)
35+
# Create a sales order
36+
self.sale_order = self.env["sale.order"].create(
37+
{
38+
"partner_id": self.partner.id,
39+
"carrier_id": self.carrier.id,
40+
}
41+
)
42+
# Create a sales order line
43+
self.sale_order_line = self.env["sale.order.line"].create(
44+
{
45+
"order_id": self.sale_order.id,
46+
"product_id": self.product.id,
47+
"product_uom_qty": 5.0,
48+
"price_unit": 100.0,
49+
"discount": 10.0,
50+
}
51+
)
52+
53+
def test_declared_value_copied_to_picking(self):
54+
"""Test that declared values are copied from sale order to picking."""
55+
# Confirm the sales order
56+
self.sale_order.action_confirm()
57+
# Get the picking created from the sales order
58+
picking = self.sale_order.picking_ids[0]
59+
# Check that the picking has the sale_id field set
60+
self.assertEqual(picking.sale_id, self.sale_order)
61+
# Check that the move has the sale_unit_price and discount fields set
62+
move = picking.move_lines[0]
63+
self.assertEqual(move.sale_unit_price, 100.0)
64+
self.assertEqual(move.discount, 10.0)
65+
# Check that the move has the price_subtotal field set with discount applied
66+
# 100.0 * (1 - 10.0 / 100.0) * 5.0 = 90.0 * 5.0 = 450.0
67+
self.assertEqual(move.price_subtotal, 450.0)
68+
# Check that the picking has the declared_amount field set from carrier
69+
self.assertEqual(picking.declared_amount, 80.0)
70+
# Check that the picking has the amount_total field set with declared_amount applied
71+
# 450.0 * 80.0 / 100.0 = 360.0
72+
self.assertEqual(picking.amount_total, 360.0)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<!-- Copyright 2025 Nitrokey GmbH
3+
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
4+
<odoo>
5+
<!-- Add declared amount field to the delivery.carrier form view -->
6+
<record id="view_delivery_carrier_form_inherit_declared_value" model="ir.ui.view">
7+
<field name="name">delivery.carrier.form.inherit.declared.value</field>
8+
<field name="model">delivery.carrier</field>
9+
<field name="inherit_id" ref="delivery.view_delivery_carrier_form" />
10+
<field name="arch" type="xml">
11+
<field name="integration_level" position="after">
12+
<field name="declared_amount" />
13+
</field>
14+
</field>
15+
</record>
16+
</odoo>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<!-- Copyright 2025 Nitrokey GmbH
3+
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
4+
<odoo>
5+
<!-- Add declared value fields to the stock move form view -->
6+
<record id="view_move_form_inherit_declared_value" model="ir.ui.view">
7+
<field name="name">stock.move.form.inherit.declared.value</field>
8+
<field name="model">stock.move</field>
9+
<field name="inherit_id" ref="stock.view_move_form" />
10+
<field name="arch" type="xml">
11+
<field name="product_uom" position="after">
12+
<field name="currency_id" invisible="1" />
13+
<field name="sale_line_id" invisible="1" />
14+
<field name="sale_unit_price" attrs="{'invisible': [('sale_line_id', '=', False)]}" />
15+
<field name="discount" attrs="{'invisible': [('sale_line_id', '=', False)]}" />
16+
<field name="price_subtotal" widget="monetary"
17+
attrs="{'invisible': [('sale_line_id', '=', False)]}" />
18+
</field>
19+
</field>
20+
</record>
21+
</odoo>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<!-- Copyright 2025 Nitrokey GmbH
3+
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
4+
<odoo>
5+
<!-- Add declared value fields to the stock.picking form view -->
6+
<record id="view_picking_form_inherit_declared_value" model="ir.ui.view">
7+
<field name="name">stock.picking.form.inherit.declared.value</field>
8+
<field name="model">stock.picking</field>
9+
<field name="inherit_id" ref="stock.view_picking_form" />
10+
<field name="arch" type="xml">
11+
<!-- Add currency field (invisible) for monetary fields -->
12+
<field name="origin" position="after">
13+
<field name="currency_id" invisible="1" />
14+
<field name="sale_id" readonly="1" attrs="{'invisible': [('sale_id', '=', False)]}" />
15+
</field>
16+
17+
<!-- Add declared value fields to the move lines -->
18+
<xpath expr="//field[@name='move_ids_without_package']/tree" position="inside">
19+
<field name="currency_id" invisible="1" />
20+
<field name="sale_line_id" invisible="1" />
21+
<field name="sale_unit_price" attrs="{'column_invisible': [('parent.sale_id', '=', False)]}" optional="hide" />
22+
<field name="discount" attrs="{'column_invisible': [('parent.sale_id', '=', False)]}" optional="hide" />
23+
<field name="price_subtotal" widget="monetary"
24+
attrs="{'column_invisible': [('parent.sale_id', '=', False)]}" optional="hide" />
25+
</xpath>
26+
27+
<!-- Add declared amount and total declared value to the Additional Info page -->
28+
<xpath expr="//page[@name='extra']" position="inside">
29+
<group name="declared_value" string="Declared Value">
30+
<field name="declared_amount" attrs="{'invisible': [('sale_id', '=', False)]}" />
31+
<field name="amount_total" widget="monetary"
32+
attrs="{'invisible': [('sale_id', '=', False)]}" />
33+
</group>
34+
</xpath>
35+
</field>
36+
</record>
37+
</odoo>

0 commit comments

Comments
 (0)
Please sign in to comment.