feat: Add subcontract lot generation wizard and UI buttons to stock pickings and moves, including validation for cancelled manufacturing orders.

This commit is contained in:
Suherdy Yacob 2026-01-28 13:45:34 +07:00
parent 56cd97ef27
commit de4f502d01
6 changed files with 149 additions and 0 deletions

View File

@ -5,6 +5,7 @@ This Odoo 19 module provides automated lot/serial number generation specifically
## Features ## Features
- **Auto-Generate Lots**: Automatically generates lot/serial numbers for subcontract receipt moves based on the product's configured sequence. - **Auto-Generate Lots**: Automatically generates lot/serial numbers for subcontract receipt moves based on the product's configured sequence.
- **Validation Check**: Prevents lot generation if the linked Subcontract Manufacturing Order is cancelled, alerting the user to potential data inconsistency.
- **Wizard Support**: Includes a "Generate Subcontract Lots" wizard for manual control and batch generation. - **Wizard Support**: Includes a "Generate Subcontract Lots" wizard for manual control and batch generation.
- **Validation Hook**: Automatically triggers lot generation when validating a subcontracting receipt (Stock Picking). - **Validation Hook**: Automatically triggers lot generation when validating a subcontracting receipt (Stock Picking).
- **Native Compatibility**: Uses Odoo 19's standard `lot_sequence_id` on the product template, ensuring no conflict with standard features. - **Native Compatibility**: Uses Odoo 19's standard `lot_sequence_id` on the product template, ensuring no conflict with standard features.

View File

@ -1,4 +1,6 @@
from odoo import api, models, _ from odoo import api, models, _
from odoo.exceptions import UserError
import logging import logging
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@ -11,9 +13,24 @@ class StockMove(models.Model):
""" """
Auto-generate lot numbers for subcontracting moves. Auto-generate lot numbers for subcontracting moves.
This method handles the automatic lot generation for subcontracted products. This method handles the automatic lot generation for subcontracted products.
It also validates that the linked Manufacturing Order is not cancelled.
""" """
self.ensure_one() self.ensure_one()
if getattr(self, 'is_subcontract', False):
# Check for cancelled subcontract MOs
# We use getattr/safe access or rely on the fact that this module depends on mrp_subcontracting
productions = self._get_subcontract_production()
cancelled_productions = productions.filtered(lambda p: p.state == 'cancel')
if cancelled_productions:
raise UserError(_(
"Cannot generate lots for product %s.\n"
"The linked Subcontracting Manufacturing Order (e.g., %s) is cancelled.\n"
"Lots generated would not be correctly linked to the receipt quantity.\n"
"Please check the Manufacturing Order status."
) % (self.product_id.display_name, cancelled_productions[0].name))
if not self.product_id.tracking in ['lot', 'serial']: if not self.product_id.tracking in ['lot', 'serial']:
return [] return []

View File

@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_subcontract_lot_generator,subcontract.lot.generator,model_subcontract_lot_generator,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_subcontract_lot_generator subcontract.lot.generator model_subcontract_lot_generator base.group_user 1 1 1 1

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Add menu item to stock move -->
<record id="view_stock_move_form_inherit_lot_generation" model="ir.ui.view">
<field name="name">stock.move.form.inherit.lot.generation</field>
<field name="model">stock.move</field>
<field name="inherit_id" ref="stock.view_move_form"/>
<field name="arch" type="xml">
<xpath expr="//header" position="inside">
<button name="action_generate_lots_for_move"
type="object"
string="Generate Lots"
class="btn-secondary"
icon="fa-plus"
invisible="not is_subcontract"
help="Generate lot/serial numbers for this subcontract move"/>
</xpath>
</field>
</record>
</odoo>

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Add auto-generate lots button to subcontracting receipt forms -->
<record id="view_picking_form_inherit_subcontract_lots" model="ir.ui.view">
<field name="name">stock.picking.form.inherit.subcontract.lots</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_form"/>
<field name="arch" type="xml">
<!-- Add invisible fields for computed values -->
<xpath expr="//form" position="inside">
<field name="has_subcontract_moves" invisible="1"/>
</xpath>
<!-- Add the auto-generate button in the header for subcontract receipts -->
<xpath expr="//header" position="inside">
<button name="action_auto_generate_lots_subcontract"
type="object"
string="Auto Generate Lots"
class="btn-secondary"
icon="fa-plus"
invisible="picking_type_code != 'incoming' or not has_subcontract_moves"
help="Automatically generate lot/serial numbers for subcontracted products"/>
<button name="action_open_subcontract_lot_wizard"
type="object"
string="Advanced Lot Generation"
class="btn-secondary"
icon="fa-cogs"
invisible="picking_type_code != 'incoming' or not has_subcontract_moves"
help="Advanced lot generation with custom options"/>
</xpath>
</field>
</record>
<!-- Enhance the move lines view to show auto-generate option -->
<record id="view_stock_move_line_detailed_operation_inherit" model="ir.ui.view">
<field name="name">stock.move.line.detailed.operation.inherit.subcontract</field>
<field name="model">stock.move.line</field>
<field name="inherit_id" ref="stock.view_stock_move_line_detailed_operation_tree"/>
<field name="arch" type="xml">
<!-- Add a button in the move line tree for individual lot generation -->
<xpath expr="//field[@name='lot_name']" position="after">
<button name="action_open_lot_generator"
type="object"
icon="fa-plus"
class="btn-link"
context="{'default_product_id': product_id, 'default_move_id': move_id}"
invisible="not product_id"
help="Generate lot/serial numbers"/>
</xpath>
</field>
</record>
<!-- Add a smart button to show generated lots for subcontract moves -->
<record id="view_picking_form_subcontract_lots_smart_button" model="ir.ui.view">
<field name="name">stock.picking.form.subcontract.lots.smart.button</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="inside">
<button type="object"
name="action_view_generated_lots"
class="oe_stat_button"
icon="fa-barcode"
invisible="picking_type_code != 'incoming' or not has_subcontract_moves">
<field name="subcontract_lot_count" widget="statinfo" string="Generated Lots"/>
</button>
</xpath>
</field>
</record>
</odoo>

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Subcontract Lot Generator Wizard Form -->
<record id="view_subcontract_lot_generator_form" model="ir.ui.view">
<field name="name">subcontract.lot.generator.form</field>
<field name="model">subcontract.lot.generator</field>
<field name="arch" type="xml">
<form string="Generate Lots for Subcontract">
<group>
<group>
<field name="picking_id" readonly="1"/>
<field name="move_id" readonly="1"/>
<field name="product_id" readonly="1"/>
<field name="tracking" invisible="1"/>
</group>
<group>
<field name="quantity"/>
<field name="lot_count" readonly="tracking == 'serial'"/>
<field name="use_sequence"/>
</group>
</group>
<footer>
<button name="action_generate_lots" type="object" string="Generate Lots" class="btn-primary"/>
<button string="Cancel" class="btn-secondary" special="cancel"/>
</footer>
</form>
</field>
</record>
<!-- Action for the wizard -->
<record id="action_subcontract_lot_generator" model="ir.actions.act_window">
<field name="name">Generate Subcontract Lots</field>
<field name="res_model">subcontract.lot.generator</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="context">{
'default_picking_id': active_id,
}</field>
</record>
</odoo>