diff options
| author | Azka Nathan <darizkyfaz@gmail.com> | 2025-02-24 09:57:56 +0700 |
|---|---|---|
| committer | Azka Nathan <darizkyfaz@gmail.com> | 2025-02-24 09:57:56 +0700 |
| commit | c1810b315d820a184db47d551b39700ce00d1440 (patch) | |
| tree | 7f7e93fcacdda6d4b607a4efa795cca0cc21d905 | |
| parent | 99b252edaefc372fcd4ef59e065284d8feeb669c (diff) | |
push wms
| -rw-r--r-- | indoteknik_custom/models/sales_order_koli.py | 1 | ||||
| -rw-r--r-- | indoteknik_custom/models/stock_backorder_confirmation.py | 63 | ||||
| -rw-r--r-- | indoteknik_custom/models/stock_immediate_transfer.py | 8 | ||||
| -rw-r--r-- | indoteknik_custom/models/stock_picking.py | 192 | ||||
| -rwxr-xr-x | indoteknik_custom/views/sale_order.xml | 2 | ||||
| -rw-r--r-- | indoteknik_custom/views/stock_picking.xml | 5 |
6 files changed, 110 insertions, 161 deletions
diff --git a/indoteknik_custom/models/sales_order_koli.py b/indoteknik_custom/models/sales_order_koli.py index 02e85256..c782a40e 100644 --- a/indoteknik_custom/models/sales_order_koli.py +++ b/indoteknik_custom/models/sales_order_koli.py @@ -22,4 +22,5 @@ class SalesOrderKoli(models.Model): ) koli_id = fields.Many2one('check.koli', string='Koli') picking_id = fields.Many2one('stock.picking', string='Picking') + state = fields.Selection([('not_delivered', 'Not Delivered'), ('delivered', 'Delivered')], string='Status', default='not_delivered') diff --git a/indoteknik_custom/models/stock_backorder_confirmation.py b/indoteknik_custom/models/stock_backorder_confirmation.py index 0fd7c34e..f4da4cb5 100644 --- a/indoteknik_custom/models/stock_backorder_confirmation.py +++ b/indoteknik_custom/models/stock_backorder_confirmation.py @@ -5,46 +5,29 @@ class StockBackorderConfirmation(models.TransientModel): _inherit = 'stock.backorder.confirmation' def process(self): - res = super(StockBackorderConfirmation, self).process() - pickings_to_do = self.env['stock.picking'] + pickings_not_to_do = self.env['stock.picking'] for line in self.backorder_confirmation_line_ids: - if line.to_backorder: + line.picking_id.send_mail_bills() + line.picking_id.send_koli_to_so() + if line.to_backorder is True: pickings_to_do |= line.picking_id - - for pick in pickings_to_do: - # Mencari backorder yang baru terbentuk - backorder = self.env['stock.picking'].search([('backorder_id', '=', pick.id)], limit=1) - - if backorder: - # Cari BU/OUT terbaru berdasarkan sale_id - latest_out_picking = self.env['stock.picking'].search([ - ('sale_id', '=', pick.sale_id.id), - ('picking_type_id.code', '=', 'outgoing') - ], order='id desc', limit=1) - - # Update linked_out_picking_id pada backorder BU/PICK - if latest_out_picking: - backorder.linked_out_picking_id = latest_out_picking.id - else: - backorder.linked_out_picking_id = pick.linked_out_picking_id - - # 🚀 Cek apakah ada backorder baru dari BU/OUT - for pick in self.env['stock.picking'].search([ - ('picking_type_id.code', '=', 'outgoing'), - ('backorder_id', '!=', False) - ]): - # Backorder BU/OUT terbaru - latest_out_backorder = self.env['stock.picking'].search([ - ('backorder_id', '=', pick.id) - ], order='id desc', limit=1) - - if latest_out_backorder: - # 🚀 Update semua BU/PICK yang belum `done` atau `cancel` - self.env['stock.picking'].search([ - ('sale_id', '=', pick.sale_id.id), - ('picking_type_id.code', '=', 'incoming'), - ('state', 'not in', ['done', 'cancel']) - ]).write({'linked_out_picking_id': latest_out_backorder.id}) - - return res + else: + pickings_not_to_do |= line.picking_id + + for pick_id in pickings_not_to_do: + moves_to_log = {} + for move in pick_id.move_lines: + if float_compare(move.product_uom_qty, + move.quantity_done, + precision_rounding=move.product_uom.rounding) > 0: + moves_to_log[move] = (move.quantity_done, move.product_uom_qty) + pick_id._log_less_quantities_than_expected(moves_to_log) + + pickings_to_validate = self.env.context.get('button_validate_picking_ids') + if pickings_to_validate: + pickings_to_validate = self.env['stock.picking'].browse(pickings_to_validate).with_context(skip_backorder=True) + if pickings_not_to_do: + pickings_to_validate = pickings_to_validate.with_context(picking_ids_not_to_backorder=pickings_not_to_do.ids) + return pickings_to_validate.button_validate() + return True diff --git a/indoteknik_custom/models/stock_immediate_transfer.py b/indoteknik_custom/models/stock_immediate_transfer.py index 4be0dff2..ec00df7b 100644 --- a/indoteknik_custom/models/stock_immediate_transfer.py +++ b/indoteknik_custom/models/stock_immediate_transfer.py @@ -5,25 +5,25 @@ class StockImmediateTransfer(models.TransientModel): _inherit = 'stock.immediate.transfer' def process(self): - """Override process method to add send_mail_bills logic.""" pickings_to_do = self.env['stock.picking'] pickings_not_to_do = self.env['stock.picking'] for line in self.immediate_transfer_line_ids: if line.to_immediate is True: + line.picking_id.send_mail_bills() + line.picking_id.send_koli_to_so() pickings_to_do |= line.picking_id else: pickings_not_to_do |= line.picking_id for picking in pickings_to_do: - picking.send_mail_bills() - # If still in draft => confirm and assign if picking.state == 'draft': picking.action_confirm() if picking.state != 'assigned': picking.action_assign() if picking.state != 'assigned': raise UserError(_("Could not reserve all requested products. Please use the 'Mark as Todo' button to handle the reservation manually.")) + for move in picking.move_lines.filtered(lambda m: m.state not in ['done', 'cancel']): for move_line in move.move_line_ids: move_line.qty_done = move_line.product_uom_qty @@ -33,4 +33,6 @@ class StockImmediateTransfer(models.TransientModel): pickings_to_validate = self.env['stock.picking'].browse(pickings_to_validate) pickings_to_validate = pickings_to_validate - pickings_not_to_do return pickings_to_validate.with_context(skip_immediate=True).button_validate() + return True + diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 02ce819f..f3af00d9 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -125,7 +125,6 @@ class StockPicking(models.Model): ], string='Status Reserve', readonly=True, tracking=True, help="The current state of the stock picking.") notee = fields.Text(string="Note") quantity_koli = fields.Float(string="Quantity Koli", copy=False) - source_koli_id = fields.Many2one('stock.picking', string="Source Koli") @api.model def _compute_dokumen_tanda_terima(self): @@ -170,45 +169,17 @@ class StockPicking(models.Model): lalamove_image_url = fields.Char(string="Lalamove Image URL") lalamove_image_html = fields.Html(string="Lalamove Image", compute="_compute_lalamove_image_html") - total_so_koli = fields.Integer(compute='_compute_total_so_koli', string="Total SO Koli") total_koli = fields.Integer(compute='_compute_total_koli', string="Total Koli") total_koli_display = fields.Char(compute='_compute_total_koli_display', string="Total Koli Display") linked_out_picking_id = fields.Many2one('stock.picking', string="Linked BU/OUT", copy=False) - backorder_picking_id = fields.Many2one('stock.picking', string="Backorder Picking", copy=False) - - def action_create_backorder(self): - """ Override method to handle backorder logic automatically """ - backorder = super(StockPicking, self).action_create_backorder() - - for picking in self: - if 'BU/PICK/' in picking.name: - # Jika BU/PICK memiliki BU/OUT yang terhubung - if picking.linked_out_picking_id: - out_picking = picking.linked_out_picking_id - out_backorder = out_picking.backorder_picking_id - - # Jika BU/OUT belum punya backorder, hubungkan BU/PICK backorder ke BU/OUT lama - if not out_backorder: - backorder.linked_out_picking_id = out_picking - else: - # Jika BU/OUT sudah punya backorder, hubungkan ke backorder BU/OUT - backorder.linked_out_picking_id = out_backorder - - elif 'BU/OUT/' in picking.name: - # Jika BU/OUT membuat backorder, update semua BU/PICK yang terhubung ke BU/OUT lama - pickings_to_update = self.env['stock.picking'].search([('linked_out_picking_id', '=', picking.id)]) - for pick in pickings_to_update: - pick.linked_out_picking_id = backorder - - return backorder - + total_so_koli = fields.Integer(compute='_compute_total_so_koli', string="Total SO Koli") - @api.depends('total_so_koli') # Sesuaikan dengan field yang relevan + @api.depends('total_so_koli') def _compute_total_so_koli(self): for picking in self: - picking.total_so_koli = self.env['check.koli'].search_count([('picking_id.linked_out_picking_id', '=', picking.id)]) + picking.total_so_koli = self.env['sales.order.koli'].search_count([('picking_id.linked_out_picking_id', '=', picking.id), ('state', '!=', 'delivered')]) - @api.depends('total_koli') # Sesuaikan dengan field yang relevan + @api.depends('total_koli') def _compute_total_koli(self): for picking in self: picking.total_koli = self.env['scan.koli'].search_count([('picking_id', '=', picking.id)]) @@ -425,7 +396,7 @@ class StockPicking(models.Model): "name": order_line.product_id.name, "description": order_line.name, "value": order_line.price_unit, - "quantity": move_line.qty_done, # Menggunakan qty_done dari move_line + "quantity": move_line.qty_done, "weight": order_line.weight }) @@ -820,7 +791,7 @@ class StockPicking(models.Model): raise UserError('Quantity Done melebihi Quantity Onhand') def button_validate(self): - if self.total_koli != self.total_so_koli: + if self.total_koli > self.total_so_koli: raise UserError(_("Total Koli (%s) dan Total SO Koli (%s) tidak sama! Harap periksa kembali.") % (self.total_koli, self.total_so_koli)) if not self.env.user.is_logistic_approver and self.env.context.get('active_model') == 'stock.picking': @@ -870,8 +841,7 @@ class StockPicking(models.Model): self.validation_minus_onhand_quantity() self.responsible = self.env.user.id - if self.picking_type_code == 'internal' and 'BU/PICK/' in self.name: - self.send_koli_to_so() + # self.send_koli_to_so() if self.picking_type_code == 'outgoing' and 'BU/OUT/' in self.name: self.check_koli() res = super(StockPicking, self).button_validate() @@ -890,21 +860,29 @@ class StockPicking(models.Model): def send_koli_to_so(self): for picking in self: - for koli_line in picking.check_koli_lines: - existing_koli = self.env['sales.order.koli'].search([ - ('sale_order_id', '=', picking.sale_id.id), - ('picking_id', '=', picking.id), - ('koli_id', '=', koli_line.id) - ], limit=1) - - if not existing_koli: # Hindari duplikasi - self.env['sales.order.koli'].create({ - 'sale_order_id': picking.sale_id.id, - 'picking_id': picking.id, - 'koli_id': koli_line.id - }) - - + if picking.picking_type_code == 'internal' and 'BU/PICK/' in picking.name: + for koli_line in picking.check_koli_lines: + existing_koli = self.env['sales.order.koli'].search([ + ('sale_order_id', '=', picking.sale_id.id), + ('picking_id', '=', picking.id), + ('koli_id', '=', koli_line.id) + ], limit=1) + + if not existing_koli: + self.env['sales.order.koli'].create({ + 'sale_order_id': picking.sale_id.id, + 'picking_id': picking.id, + 'koli_id': koli_line.id + }) + + if picking.picking_type_code == 'outgoing' and 'BU/OUT/' in picking.name: + for koli_line in picking.scan_koli_lines: + existing_koli = self.env['sales.order.koli'].search([ + ('sale_order_id', '=', picking.sale_id.id), + ('koli_id', '=', koli_line.koli_id.koli_id.id) + ], limit=1) + + existing_koli.state = 'delivered' def check_qty_done_stock(self): for line in self.move_line_ids_without_package: @@ -981,62 +959,11 @@ class StockPicking(models.Model): res = super(StockPicking, self).action_cancel() return res - - def write(self, vals_list): - """ Override write method to auto-link BU/PICK to BU/OUT when necessary """ - records = super(StockPicking, self).write(vals_list) - for picking in records: - if 'BU/OUT/' in picking.name: - # Cari BU/PICK yang berhubungan berdasarkan logika tertentu - pick_picking = self.env['stock.picking'].search([ - ('name', 'like', 'BU/PICK/%'), - ('linked_out_picking_id', '=', False), - ('sale_id', '=', picking.sale_id.id) - ], limit=1) - - if pick_picking: - pick_picking.linked_out_picking_id = picking - - if 'BU/PICK/' in picking.name: - # Cari BU/PICK yang berhubungan berdasarkan logika tertentu - pick_picking = self.env['stock.picking'].search([ - ('name', 'like', 'BU/OUT/%'), - ('state', 'not in', ['cancel', 'done']), - ('sale_id', '=', picking.sale_id.id) - ], limit=1) - - if pick_picking: - pick_picking.linked_out_picking_id = picking - - return records - @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 records = super(StockPicking, self).create(vals) - for picking in records: - if 'BU/OUT/' in picking.name: - # Cari BU/PICK yang berhubungan berdasarkan logika tertentu - pick_picking = self.env['stock.picking'].search([ - ('name', 'like', 'BU/PICK'), - ('linked_out_picking_id', '=', False), - ('origin', '=', picking.origin) - ], limit=1) - - if pick_picking: - pick_picking.linked_out_picking_id = picking return records @@ -1367,6 +1294,7 @@ class CheckKoli(models.Model): copy=False, ) koli = fields.Char(string='Koli') + reserved_id = fields.Many2one('stock.picking', string='Reserved Picking') class ScanKoli(models.Model): _name = 'scan.koli' @@ -1388,6 +1316,35 @@ class ScanKoli(models.Model): compute="_compute_scan_koli_progress" ) + def unlink(self): + for scan in self: + koli = scan.koli_id.koli_id + if koli: + # Hapus reserved_id saat scan dihapus + koli.reserved_id = False + + # Ambil semua scan koli yang masih ada dan memiliki picking_id yang sama + remaining_scans = self.env['scan.koli'].search([ + ('id', '!=', scan.id), # Kecuali scan yang sedang dihapus + ('koli_id.picking_id', '=', koli.picking_id.id) + ]) + + # Jika tidak ada scan lain yang memiliki picking_id yang sama, hapus linked_out_picking_id + if not remaining_scans: + koli.picking_id.linked_out_picking_id = False + + return super(ScanKoli, self).unlink() + + @api.onchange('koli_id','scan_koli_progress') + def onchange_koli_id(self): + if not self.koli_id: + return + + for scan in self: + if scan.koli_id.koli_id.picking_id.group_id.id != scan.picking_id.group_id.id: + scan.koli_id.koli_id.reserved_id = scan.picking_id.id.origin + scan.koli_id.koli_id.picking_id.linked_out_picking_id = scan.picking_id.id.origin + def _compute_scan_koli_progress(self): """ Menghitung progres scan koli dalam format 'X/Y' """ for scan in self: @@ -1397,18 +1354,12 @@ class ScanKoli(models.Model): total_so_koli = scan.picking_id.total_so_koli scan.scan_koli_progress = f"{scan_index}/{total_so_koli}" if total_so_koli else "0/0" - @api.model_create_multi - def create(self, vals_list): - """ Override create untuk update progress scan setelah scan koli ditambahkan """ - records = super(ScanKoli, self).create(vals_list) - for record in records: - record._compute_scan_koli_progress() - return records - @api.constrains('picking_id', 'picking_id.total_so_koli') def _check_koli_validation(self): """ Validasi jika jumlah scan koli melebihi total SO koli """ for scan in self: + scan.koli_id.koli_id.reserved_id = scan.picking_id.id + scan.koli_id.koli_id.picking_id.linked_out_picking_id = scan.picking_id.id total_scans = len(scan.picking_id.scan_koli_lines) if total_scans > scan.picking_id.total_so_koli: raise UserError(_("Jumlah scan koli melebihi total SO koli!")) @@ -1418,9 +1369,20 @@ class ScanKoli(models.Model): if not self.koli_id: return - source_koli_so = self.picking_id.ids # Picking asal dari Koli yang dipilih - source_koli = self.koli_id.picking_id.linked_out_picking_id.ids - - # Cek apakah source_koli ditemukan + source_koli_so = self.picking_id.group_id.id + source_koli = self.koli_id.picking_id.group_id.id + if source_koli_so != source_koli: raise UserError(_('Koli tidak sesuai, pastikan picking terkait benar!')) + + @api.onchange('koli_id') + def _onchange_koliii(self): + if self.koli_id and self.picking_id: + existing_koli = self.env['scan.koli'].search([ + ('picking_id', '=', self.picking_id.id), + ('koli_id', '=', self.koli_id.id), + ('id', '!=', self.id.origin) # Hindari validasi saat edit data + ]) + if existing_koli: + self.koli_id = False # Reset field koli_id agar pengguna tidak bisa memilihnya + raise UserError(f"Koli {existing_koli.koli_id.name} sudah dipindai dalam picking ini!")
\ No newline at end of file diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index 877208b0..4d31b072 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -401,6 +401,8 @@ <field name="arch" type="xml"> <tree editable="top" create="false" delete="false"> <field name="koli_id" readonly="1"/> + <field name="picking_id" readonly="1"/> + <field name="state" readonly="1"/> </tree> </field> </record> diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 2a11459c..1b3406ec 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -73,10 +73,8 @@ <field name="dokumen_tanda_terima"/> <field name="dokumen_pengiriman"/> <field name="quantity_koli" attrs="{'invisible': [('location_dest_id', '!=', 60)]}"/> - <!-- <field name="source_koli_id" attrs="{'invisible': [('picking_type_code', '!=', 'outgoing')]}"/> --> <field name="total_koli_display" readonly="1"/> <field name="linked_out_picking_id" readonly="1"/> - <field name="backorder_picking_id" readonly="1"/> </field> <field name="weight_uom_name" position="after"> <group> @@ -219,7 +217,7 @@ <field name="model">scan.koli</field> <field name="arch" type="xml"> <tree editable="bottom"> - <field name="koli_id"/> + <field name="koli_id" domain="[('state', '=', 'not_delivered')]"/> <field name="scan_koli_progress"/> </tree> </field> @@ -231,6 +229,7 @@ <field name="arch" type="xml"> <tree editable="bottom"> <field name="koli"/> + <field name="reserved_id"/> </tree> </field> </record> |
