diff options
| author | trisusilo48 <tri.susilo@altama.co.id> | 2025-02-24 11:32:06 +0700 |
|---|---|---|
| committer | trisusilo48 <tri.susilo@altama.co.id> | 2025-02-24 11:32:06 +0700 |
| commit | 7df979b8d5312bb90d13d338f2a787dc35373f17 (patch) | |
| tree | 12a5fd7b487781b52900505a2cffa999da692f9f /indoteknik_custom/models/stock_picking.py | |
| parent | a9c4cd0c5ac694074f0e3a4359182a97f27f542e (diff) | |
| parent | 38f8ddd9aaaad58c8d7ea27235cd109ba288693d (diff) | |
Merge branch 'odoo-production' into feature/integrasi_biteship
# Conflicts:
# indoteknik_custom/models/__init__.py
# indoteknik_custom/models/product_template.py
# indoteknik_custom/models/sale_order.py
# indoteknik_custom/security/ir.model.access.csv
Diffstat (limited to 'indoteknik_custom/models/stock_picking.py')
| -rw-r--r-- | indoteknik_custom/models/stock_picking.py | 302 |
1 files changed, 251 insertions, 51 deletions
diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 49c17788..1690a4ed 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -23,7 +23,8 @@ _biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1l 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') @@ -216,7 +217,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: @@ -524,18 +526,52 @@ class StockPicking(models.Model): def check_state_reserve(self): pickings = self.search([ ('state', 'not in', ['cancel', 'draft', 'done']), + ('picking_type_code', '=', 'outgoing'), + ('name', 'ilike', 'BU/OUT/'), + ]) + + count = self.search_count([ + ('state', 'not in', ['cancel', 'draft', 'done']), ('picking_type_code', '=', 'outgoing') ]) for picking in pickings: - fullfillments = self.env['sales.order.fullfillment'].search([ - ('sales_order_id', '=', picking.sale_id.id) + fullfillments = self.env['sales.order.fulfillment.v2'].search([ + ('sale_order_id', '=', picking.sale_id.id) ]) picking.state_reserve = 'ready' picking.date_reserved = picking.date_reserved or datetime.datetime.utcnow() - if any(rec.reserved_from not in ['Inventory On Hand', 'Reserved from stock', 'Free Stock'] for rec in fullfillments): + if any(rec.so_qty != rec.reserved_stock_qty for rec in fullfillments): + picking.state_reserve = 'waiting' + picking.date_reserved = '' + + self.check_state_reserve_backorder() + + def check_state_reserve_backorder(self): + pickings = self.search([ + ('backorder_id', '!=', False), + ('name', 'ilike', 'BU/OUT/'), + ('picking_type_code', '=', 'outgoing'), + ('state', 'not in', ['cancel', 'draft', 'done']) + ]) + + count = self.search_count([ + ('backorder_id', '!=', False), + ('picking_type_code', '=', 'outgoing'), + ('state', 'not in', ['cancel', 'draft', 'done']) + ]) + + for picking in pickings: + fullfillments = self.env['sales.order.fulfillment.v2'].search([ + ('sale_order_id', '=', picking.sale_id.id) + ]) + + picking.state_reserve = 'ready' + picking.date_reserved = picking.date_reserved or datetime.datetime.utcnow() + + if any(rec.so_qty != rec.reserved_stock_qty for rec in fullfillments): picking.state_reserve = 'waiting' picking.date_reserved = '' @@ -867,8 +903,26 @@ class StockPicking(models.Model): self.calculate_line_no() self.date_done = datetime.datetime.utcnow() self.state_reserve = 'done' + self.send_mail_bills() 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: @@ -933,10 +987,39 @@ class StockPicking(models.Model): @api.model def create(self, vals): self._use_faktur(vals) + if vals.get('picking_type_code') == 'incoming' and vals.get('location_dest_id') == 58: + if 'name' in vals and vals['name'].startswith('BU/IN/'): + vals['name'] = vals['name'].replace('BU/IN/', 'BU/INPUT/', 1) + + if vals.get('picking_type_code') == 'internal' and vals.get('location_id') == 58: + if 'name' in vals and vals['name'].startswith('BU/INT'): + new_name = vals['name'].replace('BU/INT', 'BU/IN', 1) + # Periksa apakah nama sudah ada + if self.env['stock.picking'].search_count([('name', '=', new_name), ('company_id', '=', vals.get('company_id'))]) > 0: + new_name = f"{new_name}-DUP" + vals['name'] = new_name return super(StockPicking, self).create(vals) def write(self, vals): self._use_faktur(vals) + for picking in self: + # Periksa apakah kondisi terpenuhi saat data diubah + if (vals.get('picking_type_code', picking.picking_type_code) == 'incoming' and + vals.get('location_dest_id', picking.location_dest_id.id) == 58): + if 'name' in vals or picking.name.startswith('BU/IN/'): + name_to_modify = vals.get('name', picking.name) + if name_to_modify.startswith('BU/IN/'): + vals['name'] = name_to_modify.replace('BU/IN/', 'BU/INPUT/', 1) + + if (vals.get('picking_type_code', picking.picking_type_code) == 'internal' and + vals.get('location_id', picking.location_id.id) == 58): + name_to_modify = vals.get('name', picking.name) + if name_to_modify.startswith('BU/INT'): + new_name = name_to_modify.replace('BU/INT', 'BU/IN', 1) + # Periksa apakah nama sudah ada + if self.env['stock.picking'].search_count([('name', '=', new_name), ('company_id', '=', picking.company_id.id)]) > 0: + new_name = f"{new_name}-DUP" + vals['name'] = new_name return super(StockPicking, self).write(vals) def _use_faktur(self, vals): @@ -1138,48 +1221,165 @@ 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' tidak ada di operations. " + ) % 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." + ) % (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." + ) % (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 |
