diff options
| author | Azka Nathan <darizkyfaz@gmail.com> | 2025-01-16 10:30:17 +0700 |
|---|---|---|
| committer | Azka Nathan <darizkyfaz@gmail.com> | 2025-01-16 10:30:17 +0700 |
| commit | 3d672f12eecd77af9a805cebd8ce3a7083957a1f (patch) | |
| tree | ff079f6e18d9d38653aec79a55ef4f89778728a0 | |
| parent | 27f333cc14b8673b3c46e1969736d2fb60d9924f (diff) | |
wms push
| -rwxr-xr-x | indoteknik_custom/__manifest__.py | 1 | ||||
| -rw-r--r-- | indoteknik_custom/models/stock_move.py | 4 | ||||
| -rw-r--r-- | indoteknik_custom/models/stock_picking.py | 234 | ||||
| -rwxr-xr-x | indoteknik_custom/security/ir.model.access.csv | 2 | ||||
| -rw-r--r-- | indoteknik_custom/views/stock_backorder_confirmation_views.xml | 14 | ||||
| -rw-r--r-- | indoteknik_custom/views/stock_picking.xml | 23 |
6 files changed, 226 insertions, 52 deletions
diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index d44f85db..1ffe9419 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -150,6 +150,7 @@ 'views/form_vendor_approval_multi_approve.xml', 'views/form_vendor_approval_multi_reject.xml', 'views/user_pengajuan_tempo.xml', + 'views/stock_backorder_confirmation_views.xml', 'report/report.xml', 'report/report_banner_banner.xml', 'report/report_banner_banner2.xml', diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index e1d4e74c..6b631713 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -12,9 +12,13 @@ class StockMove(models.Model): default=lambda self: self.product_id.print_barcode, ) qr_code_variant = fields.Binary("QR Code Variant", compute='_compute_qr_code_variant') + barcode = fields.Char(string='Barcode', related='product_id.barcode') def _compute_qr_code_variant(self): for rec in self: + if rec.picking_id.picking_type_code == 'outgoing' and rec.picking_id and rec.picking_id.origin and rec.picking_id.origin.startswith('SO/'): + rec.qr_code_variant = rec.product_id.qr_code_variant + rec.print_barcode = True if rec.print_barcode and rec.print_barcode == True and rec.product_id and rec.product_id.qr_code_variant: rec.qr_code_variant = rec.product_id.qr_code_variant else: diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index cd330aeb..05a7ee4a 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -16,7 +16,8 @@ _logger = logging.getLogger(__name__) class StockPicking(models.Model): _inherit = 'stock.picking' - # check_product_lines = fields.One2many('check.product', 'picking_id', string='Check Product', auto_join=True) + check_product_lines = fields.One2many('check.product', 'picking_id', string='Check Product', auto_join=True) + barcode_product_lines = fields.One2many('barcode.product', 'picking_id', string='Barcode Product', auto_join=True) is_internal_use = fields.Boolean('Internal Use', help='flag which is internal use or not') account_id = fields.Many2one('account.account', string='Account') efaktur_id = fields.Many2one('vit.efaktur', string='Faktur Pajak') @@ -176,7 +177,8 @@ class StockPicking(models.Model): pickings = self.env['stock.picking'].search([ ('picking_type_code', '=', 'outgoing'), ('state', '=', 'done'), - ('carrier_id', '=', 9) + ('carrier_id', '=', 9), + ('lalamove_order_id', '!=', False) ]) for picking in pickings: try: @@ -810,7 +812,24 @@ class StockPicking(models.Model): self.date_done = datetime.datetime.utcnow() self.state_reserve = 'done' return res - + + + def check_qty_done_stock(self): + for line in self.move_line_ids_without_package: + def check_qty_per_inventory(self, product, location): + quant = self.env['stock.quant'].search([ + ('product_id', '=', product.id), + ('location_id', '=', location.id), + ]) + + if quant: + return quant.quantity + + return 0 + + qty_onhand = check_qty_per_inventory(self, line.product_id, line.location_id) + if line.qty_done > qty_onhand: + raise UserError('Quantity Done melebihi Quantity Onhand') def send_mail_bills(self): if self.picking_type_code == 'incoming' and self.purchase_id: @@ -1008,48 +1027,167 @@ class StockPicking(models.Model): return f'{formatted_fastest_eta} - {formatted_longest_eta}' -# class CheckProduct(models.Model): -# _name = 'check.product' -# _description = 'Check Product' -# _order = 'picking_id, id' - -# picking_id = fields.Many2one('stock.picking', string='Picking Reference', required=True, ondelete='cascade', index=True, copy=False) -# product_id = fields.Many2one('product.product', string='Product') - - -# @api.constrains('product_id') -# def check_product_validity(self): -# """ -# Validate if the product exists in the related stock.picking's move_ids_without_package -# and ensure that the product's quantity does not exceed the available product_uom_qty. -# """ -# for record in self: -# if not record.picking_id or not record.product_id: -# continue - -# # Filter move lines in the related picking for the selected product -# moves = record.picking_id.move_ids_without_package.filtered( -# lambda move: move.product_id.id == record.product_id.id -# ) - -# if not moves: -# raise UserError(( -# "The product '%s' is not available in the related stock picking's moves. " -# "Please check and try again." -# ) % record.product_id.display_name) - -# # Calculate the total entries for the product in check.product for the same picking -# product_entries_count = self.search_count([ -# ('picking_id', '=', record.picking_id.id), -# ('product_id', '=', record.product_id.id) -# ]) - -# # Sum the product_uom_qty for all relevant moves -# total_qty_in_moves = sum(moves.mapped('product_uom_qty')) - -# # Compare the count of entries against the available quantity -# if product_entries_count > total_qty_in_moves: -# raise UserError(( -# "The product '%s' exceeds the allowable quantity (%s) in the related stock picking's moves. " -# "You can only add it %s times." -# ) % (record.product_id.display_name, total_qty_in_moves, total_qty_in_moves)) +class CheckProduct(models.Model): + _name = 'check.product' + _description = 'Check Product' + _order = 'picking_id, id' + + picking_id = fields.Many2one( + 'stock.picking', + string='Picking Reference', + required=True, + ondelete='cascade', + index=True, + copy=False, + ) + product_id = fields.Many2one('product.product', string='Product', required=True) + quantity = fields.Float(string='Quantity', default=1.0, required=True) + status = fields.Char(string='Status', compute='_compute_status') + + @api.depends('quantity') + def _compute_status(self): + for record in self: + moves = record.picking_id.move_ids_without_package.filtered( + lambda move: move.product_id.id == record.product_id.id + ) + total_qty_in_moves = sum(moves.mapped('product_uom_qty')) + + if record.quantity < total_qty_in_moves: + record.status = 'Pending' + else: + record.status = 'Done' + + + def create(self, vals): + # Create the record + record = super(CheckProduct, self).create(vals) + # Ensure uniqueness after creation + if not self.env.context.get('skip_consolidate'): + record.with_context(skip_consolidate=True)._consolidate_duplicate_lines() + return record + + def write(self, vals): + # Write changes to the record + result = super(CheckProduct, self).write(vals) + # Ensure uniqueness after writing + if not self.env.context.get('skip_consolidate'): + self.with_context(skip_consolidate=True)._consolidate_duplicate_lines() + return result + + def _sync_check_product_to_moves(self, picking): + """ + Sinkronisasi quantity_done di move_ids_without_package + dengan total quantity dari check.product berdasarkan product_id. + """ + for product_id in picking.check_product_lines.mapped('product_id'): + # Totalkan quantity dari semua baris check.product untuk product_id ini + total_quantity = sum( + line.quantity for line in picking.check_product_lines.filtered(lambda line: line.product_id == product_id) + ) + # Update quantity_done di move yang relevan + moves = picking.move_ids_without_package.filtered(lambda move: move.product_id == product_id) + for move in moves: + move.quantity_done = total_quantity + + def _consolidate_duplicate_lines(self): + """ + Consolidate duplicate lines with the same product_id under the same picking_id + and sync the total quantity to related moves. + """ + for picking in self.mapped('picking_id'): + lines_to_remove = self.env['check.product'] # Recordset untuk menyimpan baris yang akan dihapus + product_lines = picking.check_product_lines.filtered(lambda line: line.product_id) + + # Group lines by product_id + product_groups = {} + for line in product_lines: + product_groups.setdefault(line.product_id.id, []).append(line) + + for product_id, lines in product_groups.items(): + if len(lines) > 1: + # Consolidate duplicate lines + first_line = lines[0] + total_quantity = sum(line.quantity for line in lines) + + # Update the first line's quantity + first_line.with_context(skip_consolidate=True).write({'quantity': total_quantity}) + + # Add the remaining lines to the lines_to_remove recordset + lines_to_remove |= self.env['check.product'].browse([line.id for line in lines[1:]]) + + # Perform unlink after consolidation + if lines_to_remove: + lines_to_remove.unlink() + + # Sync total quantities to moves + self._sync_check_product_to_moves(picking) + + @api.onchange('product_id', 'quantity') + def check_product_validity(self): + for record in self: + if not record.picking_id or not record.product_id: + continue + + # Filter moves related to the selected product + moves = record.picking_id.move_ids_without_package.filtered( + lambda move: move.product_id.id == record.product_id.id + ) + + if not moves: + raise UserError(( + "The product '%s' is not available in the related stock picking's moves. " + "Please check and try again." + ) % record.product_id.display_name) + + total_qty_in_moves = sum(moves.mapped('product_uom_qty')) + + # Find existing lines for the same product, excluding the current line + existing_lines = record.picking_id.check_product_lines.filtered( + lambda line: line.product_id == record.product_id and line.id != record.id + ) + + if existing_lines: + # Get the first existing line + first_line = existing_lines[0] + + # Calculate the total quantity after addition + total_quantity = sum(existing_lines.mapped('quantity')) - record.quantity + + if total_quantity > total_qty_in_moves: + raise UserError(( + "Quantity Product '%s' sudah melebihi quantity demand: (%s)." + ) % (record.product_id.display_name)) + + else: + # Check if the quantity exceeds the allowed total + if record.quantity > total_qty_in_moves: + raise UserError(( + "Quantity Product '%s' sudah melebihi quantity demand: (%s)." + ) % (record.product_id.display_name)) + + # Set the quantity to the entered value + record.quantity = record.quantity + +class BarcodeProduct(models.Model): + _name = 'barcode.product' + _description = 'Barcode Product' + _order = 'picking_id, id' + + picking_id = fields.Many2one( + 'stock.picking', + string='Picking Reference', + required=True, + ondelete='cascade', + index=True, + copy=False, + ) + product_id = fields.Many2one('product.product', string='Product', required=True) + barcode = fields.Char(string='Barcode') + + @api.constrains('barcode') + def send_barcode_to_product(self): + for record in self: + if record.barcode and not record.product_id.barcode: + record.product_id.barcode = record.barcode + else: + raise UserError('Barcode sudah terisi')
\ No newline at end of file diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index 4e1ecd7d..a26bce31 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -149,6 +149,7 @@ access_sales_order_fulfillment_v2,access.sales.order.fulfillment.v2,model_sales_ access_v_move_outstanding,access.v.move.outstanding,model_v_move_outstanding,,1,1,1,1 access_va_multi_approve,access.va.multi.approve,model_va_multi_approve,,1,1,1,1 access_va_multi_reject,access.va.multi.reject,model_va_multi_reject,,1,1,1,1 +access_check_product,access.check.product,model_check_product,,1,1,1,1 access_stock_immediate_transfer,access.stock.immediate.transfer,model_stock_immediate_transfer,,1,1,1,1 access_coretax_faktur,access.coretax.faktur,model_coretax_faktur,,1,1,1,1 access_purchase_order_unlock_wizard,access.purchase.order.unlock.wizard,model_purchase_order_unlock_wizard,,1,1,1,1 @@ -157,3 +158,4 @@ access_User_pengajuan_tempo_line,access.user.pengajuan.tempo.line,model_user_pen access_user_pengajuan_tempo,access.user.pengajuan.tempo,model_user_pengajuan_tempo,,1,1,1,1 access_reject_reason_wizard,reject.reason.wizard,model_reject_reason_wizard,,1,1,1,0 access_confirm_approval_wizard,confirm.approval.wizard,model_confirm_approval_wizard,,1,1,1,0 +access_barcode_product,access.barcode.product,model_barcode_product,,1,1,1,1
\ No newline at end of file diff --git a/indoteknik_custom/views/stock_backorder_confirmation_views.xml b/indoteknik_custom/views/stock_backorder_confirmation_views.xml new file mode 100644 index 00000000..a622899e --- /dev/null +++ b/indoteknik_custom/views/stock_backorder_confirmation_views.xml @@ -0,0 +1,14 @@ +<odoo> + <record id="view_backorder_confirmation_inherit" model="ir.ui.view"> + <field name="name">stock_backorder_confirmation_inherit</field> + <field name="model">stock.backorder.confirmation</field> + <field name="inherit_id" ref="stock.view_backorder_confirmation"/> + <field name="arch" type="xml"> + <xpath expr="//group/p[1]" position="replace"> + <p> + Ada product yang quantity done nya kurang dari quantity demand. + </p> + </xpath> + </field> + </record> +</odoo> diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 8acba608..1c6ae8f7 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -132,6 +132,7 @@ <field name="sale_id" attrs="{'readonly': 1}" optional="hide"/> <field name="print_barcode" optional="hide"/> <field name="qr_code_variant" widget="image" optional="hide"/> + <field name="barcode" optional="hide"/> </field> <page name="note" position="after"> <page string="E-Faktur" name="efaktur" attrs="{'invisible': [['is_internal_use', '=', False]]}"> @@ -191,15 +192,18 @@ </group> </group> </page> - <!-- <page string="Check Product" name="check_product"> + <page string="Check Product" name="check_product"> <field name="check_product_lines"/> - </page> --> + </page> + <page string="Barcode Product" name="barcode_product" attrs="{'invisible': [('picking_type_code', '!=', 'incoming')]}"> + <field name="barcode_product_lines"/> + </page> </page> </field> </record> - <!-- <record id="check_product_tree" model="ir.ui.view"> + <record id="check_product_tree" model="ir.ui.view"> <field name="name">check.product.tree</field> <field name="model">check.product</field> <field name="arch" type="xml"> @@ -209,7 +213,18 @@ <field name="status"/> </tree> </field> - </record> --> + </record> + + <record id="barcode_product_tree" model="ir.ui.view"> + <field name="name">barcode.product.tree</field> + <field name="model">barcode.product</field> + <field name="arch" type="xml"> + <tree editable="bottom"> + <field name="product_id"/> + <field name="barcode"/> + </tree> + </field> + </record> <record id="view_stock_move_line_detailed_operation_tree_inherit" model="ir.ui.view"> <field name="name">stock.move.line.operations.tree.inherit</field> |
