summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAzka Nathan <darizkyfaz@gmail.com>2025-02-24 09:57:56 +0700
committerAzka Nathan <darizkyfaz@gmail.com>2025-02-24 09:57:56 +0700
commitc1810b315d820a184db47d551b39700ce00d1440 (patch)
tree7f7e93fcacdda6d4b607a4efa795cca0cc21d905
parent99b252edaefc372fcd4ef59e065284d8feeb669c (diff)
push wms
-rw-r--r--indoteknik_custom/models/sales_order_koli.py1
-rw-r--r--indoteknik_custom/models/stock_backorder_confirmation.py63
-rw-r--r--indoteknik_custom/models/stock_immediate_transfer.py8
-rw-r--r--indoteknik_custom/models/stock_picking.py192
-rwxr-xr-xindoteknik_custom/views/sale_order.xml2
-rw-r--r--indoteknik_custom/views/stock_picking.xml5
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>