@ -0,0 +1,48 @@ |
|||
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.svg |
|||
:target: https://www.gnu.org/licenses/agpl-3.0-standalone.html |
|||
:alt: License: AGPL-3 |
|||
|
|||
Product Stock Reservation |
|||
========================== |
|||
Allow sales users to create a product stock reservation from the sales quotation form. |
|||
|
|||
Configuration |
|||
============= |
|||
* No additional configurations needed |
|||
|
|||
License |
|||
------- |
|||
GNU AFFERO GENERAL PUBLIC LICENSE, Version 3 (AGPLv3) |
|||
(https://www.gnu.org/licenses/agpl.html) |
|||
|
|||
Company |
|||
------- |
|||
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__ |
|||
|
|||
Credits |
|||
------- |
|||
* Developer: (V16) Shikhil Raj, |
|||
(V18) Prayag K |
|||
|
|||
* Contact : odoo@cybrosys.com |
|||
|
|||
Contacts |
|||
-------- |
|||
* Mail Contact : odoo@cybrosys.com |
|||
* Website : https://cybrosys.com |
|||
|
|||
Bug Tracker |
|||
----------- |
|||
Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. |
|||
|
|||
Maintainer |
|||
========== |
|||
.. image:: https://cybrosys.com/images/logo.png |
|||
:target: https://cybrosys.com |
|||
|
|||
This module is maintained by Cybrosys Technologies. |
|||
For support and more information, please visit `Our Website <https://cybrosys.com/>`__ |
|||
|
|||
Further information |
|||
=================== |
|||
HTML Description: `<static/description/index.html>`__ |
|||
@ -0,0 +1,23 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from . import models |
|||
from . import wizard |
|||
@ -0,0 +1,47 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
{ |
|||
'name': 'Product Stock Reservation', |
|||
'version': "18.0.1.0.0", |
|||
'category': 'Sales,Warehouse', |
|||
'summary': """ Reserve Product for Sales Order.""", |
|||
'description': """This module allow sales users to create a product stock |
|||
reservation from the sales quotation form.""", |
|||
'author': "Cybrosys Techno Solutions", |
|||
'company': "Cybrosys Techno Solutions", |
|||
'maintainer': "Cybrosys Techno Solutions", |
|||
'website': "https://www.cybrosys.com", |
|||
'depends': ['sale_management', 'stock', 'mail'], |
|||
'data': [ |
|||
'security/ir.model.access.csv', |
|||
'views/sale_order_views.xml', |
|||
'views/res_config_settings_views.xml', |
|||
'wizard/sale_stock_reservation_views.xml', |
|||
'data/stock_location_data.xml', |
|||
'data/ir_sequence_data.xml', |
|||
], |
|||
'images': ['static/description/banner.png'], |
|||
'license': "AGPL-3", |
|||
'installable': True, |
|||
'auto_install': False, |
|||
'application': False |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<data noupdate="1"> |
|||
<!-- Define the sequence for stock reservations --> |
|||
<record id="seq_stock_reserved" model="ir.sequence"> |
|||
<field name="name">Sock Reserved Sequence</field> |
|||
<field name="code">stock.reserved</field> |
|||
<field name="prefix">STOCK/RES/</field> |
|||
<field name="padding">3</field> |
|||
<field name="company_id" eval="False"/> |
|||
</record> |
|||
</data> |
|||
</odoo> |
|||
@ -0,0 +1,10 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<data noupdate="0"> |
|||
<!-- Define the stock location for sale stock reservations --> |
|||
<record id="sale_stock_reservation" model="stock.location"> |
|||
<field name="name">Stock Reservation Location</field> |
|||
<field name="location_id" ref="stock.stock_location_locations"/> |
|||
</record> |
|||
</data> |
|||
</odoo> |
|||
@ -0,0 +1,9 @@ |
|||
## Module <sales_stock_reservation> |
|||
|
|||
#### 02.08.2025 |
|||
#### Version 18.0.1.0.0 |
|||
##### ADD |
|||
|
|||
- Initial commit for Product Stock Reservation |
|||
|
|||
|
|||
@ -0,0 +1,24 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from . import res_config_settings |
|||
from . import sale_order |
|||
from . import stock_reserved |
|||
@ -0,0 +1,44 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from odoo import fields, models |
|||
|
|||
|
|||
class ResConfigSettings(models.TransientModel): |
|||
""" |
|||
Inherits the Res Config Settings model to add two many-to-one fields for |
|||
selecting source and destination locations. These fields are used to |
|||
configure the source and destination locations for stock reservations. |
|||
""" |
|||
_inherit = 'res.config.settings' |
|||
|
|||
source_location_id = fields.Many2one( |
|||
"stock.location", |
|||
String="Source Location", |
|||
config_parameter='sales_stock_reservation.source_location_id', |
|||
help='This is a Many2one field that refers to the location from ' |
|||
'which the products will be sourced.') |
|||
destination_location_id = fields.Many2one( |
|||
"stock.location", |
|||
String="Destination Location", |
|||
config_parameter='sales_stock_reservation.destination_location_id', |
|||
help='This is a Many2one field that refers to the location to which ' |
|||
'the products will be delivered.') |
|||
@ -0,0 +1,160 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from odoo import fields, models |
|||
|
|||
|
|||
class SaleOrder(models.Model): |
|||
""" |
|||
Inherits the Sale Order model to add the ability to apply stock |
|||
reservation for products in draft state. This model also adds a state for |
|||
tracking the reservation status and a one-to-many relationship to the |
|||
Stock Reserved model for storing the details of the reserved stock. |
|||
""" |
|||
_inherit = "sale.order" |
|||
|
|||
apply_stock_reservation = fields.Boolean( |
|||
string="Apply stock reservation", |
|||
help="Apply stock reservation in draft") |
|||
state_reservation = fields.Selection([ |
|||
('reserved', 'Reserved'), ('cancel', 'Cancelled')], |
|||
help="Condition for visibility of buttons") |
|||
reserved_stock_ids = fields.One2many( |
|||
"stock.reserved", |
|||
"sale_order_id", |
|||
string="Reserved Stock", |
|||
help="Stock reserved details") |
|||
|
|||
def action_create_stock_reservation(self): |
|||
""" |
|||
This function creates a stock reservation based on the current sale |
|||
order's order lines. |
|||
If the sale order has order lines, the function creates a list of |
|||
tuples representing the order lines,which are used to set default |
|||
values for the stock reservation. Each tuple contains three elements: |
|||
(0, 0, {...}), where the dictionary contains values for the following |
|||
fields of the stock reservation: |
|||
|
|||
- order_line_name: a string representing the name of the sale order |
|||
and the order line, concatenated |
|||
- product_id: the ID of the product being reserved |
|||
- quantity: the quantity being reserved, in the unit of measure |
|||
specified by the order line |
|||
- unit_of_measure_id: the ID of the unit of measure being used for the |
|||
reservation |
|||
- reserve_quantity: the quantity being reserved, in the unit of |
|||
measure specified by the product |
|||
|
|||
If the sale order does not have any order lines, an empty list |
|||
created. |
|||
|
|||
The function then returns a dictionary representing an action to |
|||
create a new stock reservation. |
|||
The dictionary has the following keys: |
|||
|
|||
- name: a string representing the name of the action |
|||
- type: a string representing the type of the action (in this case, |
|||
'ir.actions.act_window') |
|||
- view_type: a string representing the type of view to use |
|||
(in this case, 'form') |
|||
- view_mode: a string representing the mode of the view |
|||
(in this case, 'form') |
|||
- res_model: a string representing the name of the model being used |
|||
(in this case,'sale.stock.reservation') |
|||
- context: a dictionary representing the context to use when creating |
|||
the stock reservation |
|||
- default_sale_order_id: the ID of the sale order being used as the |
|||
basis for the reservation |
|||
- default_stock_reservation_ids: the list of tuples representing the |
|||
order lines (or an empty list) |
|||
- view_id: the ID of the view to use (retrieved using self.env.ref()) |
|||
- target: a string representing the target for the action |
|||
(in this case, 'new') |
|||
|
|||
:return: a dictionary representing the action to create a new stock |
|||
reservation |
|||
""" |
|||
line_vals = [(0, 0, { |
|||
'order_line_name': f"{self.name}-{line.name}", |
|||
'product_id': line.product_id.id, |
|||
'quantity': line.product_uom_qty, |
|||
'unit_of_measure_id': line.product_uom.id, |
|||
'reserve_quantity': line.product_uom_qty |
|||
}) for line in self.order_line] if self.order_line else [] |
|||
return { |
|||
'name': "Stock Reservation", |
|||
'type': 'ir.actions.act_window', |
|||
'view_type': 'form', |
|||
'view_mode': 'form', |
|||
'res_model': 'sale.stock.reservation', |
|||
'context': {'default_sale_order_id': self.id, |
|||
'default_stock_reservation_ids': line_vals}, |
|||
'view_id': self.env.ref( |
|||
'sales_stock_reservation.sale_stock_reservation_view_form').id, |
|||
'target': 'new', |
|||
} |
|||
|
|||
def action_cancel_reservation(self): |
|||
""" |
|||
This function cancels a stock reservation by setting its state to |
|||
'cancel', cancelling the moves associated with the reservation's |
|||
reserved stock,and setting the status of the reserved stock to |
|||
'cancelled'. |
|||
|
|||
The function first sets the `state_reservation` field of the current |
|||
object to 'cancel'. |
|||
|
|||
It then retrieves the `move_id` field of each reserved stock associated |
|||
with the reservation using `mapped()`,and calls the `_action_cancel()` |
|||
method on the resulting recordset to cancel the moves. |
|||
|
|||
After cancelling the moves, the function sets the `status` field of each |
|||
reserved stock associated with the reservation to 'cancelled' using |
|||
`mapped()`. |
|||
|
|||
Finally, the function returns True to indicate that the cancellation |
|||
was successful. |
|||
|
|||
:return: True if the reservation was successfully cancelled, False |
|||
otherwise |
|||
""" |
|||
self.state_reservation = 'cancel' |
|||
self.reserved_stock_ids.mapped('move_id')._action_cancel() |
|||
self.mapped("reserved_stock_ids").status = 'cancelled' |
|||
return True |
|||
|
|||
def action_confirm(self): |
|||
""" |
|||
This function confirms a sale order by calling the parent |
|||
`action_confirm()` method and then cancelling any associated stock |
|||
reservation. |
|||
The function first calls the `super()` method to confirm the sale order |
|||
using the parent implementation. |
|||
It then calls the `cancel_reservation()` method to cancel any existing |
|||
stock reservation associated with the sale order. |
|||
Finally, the function returns the result of the `super()` method call to |
|||
indicate whether the sale order was successfully confirmed. |
|||
|
|||
:return: the result of the parent `action_confirm()` method call |
|||
""" |
|||
res = super().action_confirm() |
|||
self.action_cancel_reservation() |
|||
return res |
|||
@ -0,0 +1,61 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from odoo import _, api, fields, models |
|||
|
|||
|
|||
class StockReserved(models.Model): |
|||
""" |
|||
This model stores details of product reservations made in sale orders. |
|||
""" |
|||
_name = "stock.reserved" |
|||
_description = "Reserved stock details" |
|||
|
|||
name = fields.Char(string="Name", readonly="True", help="Name") |
|||
order_line_name = fields.Char(string="Order Line", readonly="True", |
|||
help="Name of order line") |
|||
product_id = fields.Many2one("product.product", string="Product", |
|||
readonly="True", help="Product reserved") |
|||
status = fields.Selection( |
|||
[('reserved', 'Reserved'), ('cancelled', 'Cancelled')], |
|||
string="Status", readonly="True", help="Status of reservation") |
|||
reserved_quantity = fields.Float(string="Reserved Quantity", |
|||
readonly="True", |
|||
help="Quantity Reserved") |
|||
sale_order_id = fields.Many2one("sale.order", string="Sale Order", |
|||
readonly="True", |
|||
help="Sale order") |
|||
move_id = fields.Many2one("stock.move", string="Move Id", readonly="True", |
|||
help="Stock move details") |
|||
|
|||
@api.model |
|||
def create(self, vals_list): |
|||
""" |
|||
Create a new record for the model and generate a sequence for the name |
|||
field if it is not provided. |
|||
:return: the result of the parent `create()` method call |
|||
""" |
|||
if vals_list.get('name', _('New')) == _('New'): |
|||
vals_list['name'] = self.env['ir.sequence'].next_by_code( |
|||
'stock.reserved' |
|||
) or _('New') |
|||
res = super().create(vals_list) |
|||
return res |
|||
|
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 628 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 210 KiB |
|
After Width: | Height: | Size: 209 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 495 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 624 B |
|
After Width: | Height: | Size: 136 KiB |
|
After Width: | Height: | Size: 214 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 310 B |
|
After Width: | Height: | Size: 929 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 542 B |
|
After Width: | Height: | Size: 576 B |
|
After Width: | Height: | Size: 733 B |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 738 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 911 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 600 B |
|
After Width: | Height: | Size: 673 B |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 462 B |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 926 B |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 878 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 653 B |
|
After Width: | Height: | Size: 800 B |
|
After Width: | Height: | Size: 905 B |
|
After Width: | Height: | Size: 189 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 839 B |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 427 B |
|
After Width: | Height: | Size: 627 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 988 B |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 875 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 142 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
After Width: | Height: | Size: 354 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 124 KiB |
|
After Width: | Height: | Size: 151 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 130 KiB |
|
After Width: | Height: | Size: 99 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 880 KiB |
|
After Width: | Height: | Size: 786 KiB |
|
After Width: | Height: | Size: 40 KiB |