summaryrefslogtreecommitdiff
path: root/indoteknik_custom/models/stock_picking.py
diff options
context:
space:
mode:
Diffstat (limited to 'indoteknik_custom/models/stock_picking.py')
-rw-r--r--indoteknik_custom/models/stock_picking.py517
1 files changed, 470 insertions, 47 deletions
diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py
index ba7a9452..4a200ac5 100644
--- a/indoteknik_custom/models/stock_picking.py
+++ b/indoteknik_custom/models/stock_picking.py
@@ -1,6 +1,8 @@
from odoo import fields, models, api, _
from odoo.exceptions import AccessError, UserError, ValidationError
from odoo.tools.float_utils import float_is_zero
+from collections import defaultdict
+from datetime import timedelta, datetime
from datetime import timedelta, datetime as waktu
from itertools import groupby
import pytz, requests, json, requests
@@ -24,6 +26,9 @@ _biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1l
class StockPicking(models.Model):
_inherit = 'stock.picking'
_order = 'final_seq ASC'
+ konfirm_koli_lines = fields.One2many('konfirm.koli', 'picking_id', string='Konfirm Koli', auto_join=True)
+ scan_koli_lines = fields.One2many('scan.koli', 'picking_id', string='Scan Koli', auto_join=True)
+ check_koli_lines = fields.One2many('check.koli', 'picking_id', string='Check Koli', 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)
@@ -71,6 +76,11 @@ class StockPicking(models.Model):
readonly=True,
copy=False
)
+ out_code = fields.Integer(
+ string="Out Code",
+ readonly=True,
+ related="id",
+ )
sj_documentation = fields.Binary(string="Dokumentasi Surat Jalan", )
paket_documentation = fields.Binary(string="Dokumentasi Paket", )
sj_return_date = fields.Datetime(string="SJ Return Date", )
@@ -96,6 +106,7 @@ class StockPicking(models.Model):
('not_paid', 'Customer belum bayar'),
('partial', 'Kirim Parsial'),
('indent', 'Indent'),
+ ('waiting_schedule', 'Menunggu Jadwal Kirim'),
('self_pickup', 'Barang belum di pickup Customer'),
('expedition_closed', 'Eskpedisi belum buka')
], string='Note Logistic', help='jika field ini diisi maka tidak akan dihitung ke lead time')
@@ -141,6 +152,18 @@ class StockPicking(models.Model):
# def _compute_show_state_approve_md(self):
# for record in self:
# record.show_state_approve_md = record.location_id.id == 47 or record.location_id.complete_name == "Virtual Locations/Gudang Selisih"
+ quantity_koli = fields.Float(string="Quantity Koli", copy=False)
+ total_mapping_koli = fields.Float(string="Total Mapping Koli", compute='_compute_total_mapping_koli')
+ so_lama = fields.Boolean('SO LAMA')
+
+ @api.depends('konfirm_koli_lines', 'konfirm_koli_lines.pick_id', 'konfirm_koli_lines.pick_id.quantity_koli')
+ def _compute_total_mapping_koli(self):
+ for record in self:
+ total = 0.0
+ for line in record.konfirm_koli_lines:
+ if line.pick_id and line.pick_id.quantity_koli:
+ total += line.pick_id.quantity_koli
+ record.total_mapping_koli = total
@api.model
def _compute_dokumen_tanda_terima(self):
@@ -185,12 +208,78 @@ 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_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)
+ total_so_koli = fields.Integer(compute='_compute_total_so_koli', string="Total SO Koli")
+
# Biteship Section
biteship_id = fields.Char(string="Biteship Respon ID")
biteship_tracking_id = fields.Char(string="Biteship Trackcking ID")
biteship_waybill_id = fields.Char(string="Biteship Waybill ID")
final_seq = fields.Float(string='Remaining Time')
+ shipping_method_so_id = fields.Many2one('delivery.carrier', string='Shipping Method SO', related='sale_id.carrier_id')
+ state_packing = fields.Selection([('not_packing', 'Belum Packing'), ('packing_done', 'Sudah Packing')], string='Packing Status')
+ @api.constrains('konfirm_koli_lines')
+ def _constrains_konfirm_koli_lines(self):
+ now = datetime.datetime.utcnow()
+ for picking in self:
+ if len(picking.konfirm_koli_lines) > 0:
+ picking.state_packing = 'packing_done'
+ else:
+ picking.state_packing = 'not_packing'
+
+ @api.constrains('scan_koli_lines')
+ def _constrains_scan_koli_lines(self):
+ now = datetime.datetime.utcnow()
+ for picking in self:
+ if len(picking.scan_koli_lines) > 0:
+ picking.driver_departure_date = now
+
+ @api.depends('total_so_koli')
+ def _compute_total_so_koli(self):
+ for picking in self:
+ if picking.state == 'done':
+ picking.total_so_koli = self.env['sales.order.koli'].search_count([('picking_id.linked_out_picking_id', '=', picking.id), ('state', '=', 'delivered')])
+ else:
+ 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')
+ def _compute_total_koli(self):
+ for picking in self:
+ picking.total_koli = self.env['scan.koli'].search_count([('picking_id', '=', picking.id)])
+
+ @api.depends('total_koli', 'total_so_koli')
+ def _compute_total_koli_display(self):
+ for picking in self:
+ picking.total_koli_display = f"{picking.total_koli} / {picking.total_so_koli}"
+
+ @api.constrains('quantity_koli')
+ def _constrains_quantity_koli(self):
+ for picking in self:
+ if not picking.linked_out_picking_id:
+ so_koli = self.env['sales.order.koli'].search([('picking_id', '=', picking.id)])
+
+ if so_koli:
+ so_koli.unlink()
+
+ for rec in picking.check_koli_lines:
+ self.env['sales.order.koli'].create({
+ 'sale_order_id': picking.sale_id.id,
+ 'picking_id': picking.id,
+ 'koli_id': rec.id,
+ })
+ else:
+ raise UserError('Tidak Bisa Mengubah Quantity Koli Karena Koli Dari Picking Ini Sudah Dipakai Di BU/OUT!')
+
+ @api.onchange('quantity_koli')
+ def _onchange_quantity_koli(self):
+ self.check_koli_lines = [(5, 0, 0)]
+ self.check_koli_lines = [(0, 0, {
+ 'koli': f"{self.name}/{str(i+1).zfill(3)}",
+ 'picking_id': self.id,
+ }) for i in range(int(self.quantity_koli))]
def schduled_update_sequance(self):
query = "SELECT update_sequance_stock_picking();"
@@ -396,7 +485,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
})
@@ -503,36 +592,14 @@ class StockPicking(models.Model):
res = super(StockPicking, self).do_unreserve()
current_time = datetime.datetime.utcnow()
self.date_unreserve = current_time
- # self.check_state_reserve()
return res
- # def check_state_reserve(self):
- # do = self.search([
- # ('state', 'not in', ['cancel', 'draft', 'done']),
- # ('picking_type_code', '=', 'outgoing')
- # ])
-
- # for rec in do:
- # rec.state_reserve = 'ready'
- # rec.date_reserved = datetime.datetime.utcnow()
-
- # for line in rec.move_ids_without_package:
- # if line.product_uom_qty > line.reserved_availability:
- # rec.state_reserve = 'waiting'
- # rec.date_reserved = ''
- # break
-
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')
+ ('picking_type_code', '=', 'internal'),
+ ('name', 'ilike', 'BU/PICK/'),
])
for picking in pickings:
@@ -552,14 +619,8 @@ class StockPicking(models.Model):
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'),
+ ('name', 'ilike', 'BU/PICK/'),
+ ('picking_type_code', '=', 'internal'),
('state', 'not in', ['cancel', 'draft', 'done'])
])
@@ -880,6 +941,35 @@ class StockPicking(models.Model):
if self.location_id.id == 47 and self.env.user.id in users_in_group.mapped('id'):
self.state_approve_md = 'done'
+ threshold_datetime = waktu(2025, 4, 11, 6, 26)
+
+ if (len(self.konfirm_koli_lines) == 0
+ and 'BU/OUT/' in self.name
+ and self.picking_type_code == 'outgoing'
+ and self.create_date > threshold_datetime
+ and not self.so_lama):
+ raise UserError(_("Tidak ada Mapping koli! Harap periksa kembali."))
+
+ if (len(self.scan_koli_lines) == 0
+ and 'BU/OUT/' in self.name
+ and self.picking_type_code == 'outgoing'
+ and self.create_date > threshold_datetime
+ and not self.so_lama):
+ raise UserError(_("Tidak ada scan koli! Harap periksa kembali."))
+
+ # if self.driver_departure_date == False and 'BU/OUT/' in self.name and self.picking_type_code == 'outgoing':
+ # raise UserError(_("Isi Driver Departure Date dulu sebelum validate"))
+
+ if len(self.check_koli_lines) == 0 and 'BU/PICK/' in self.name:
+ raise UserError(_("Tidak ada koli! Harap periksa kembali."))
+
+ if len(self.check_product_lines) == 0 and 'BU/PICK/' in self.name:
+ raise UserError(_("Tidak ada Check Product! Harap periksa kembali."))
+
+ 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.t1otal_so_koli))
+
if not self.env.user.is_logistic_approver and self.env.context.get('active_model') == 'stock.picking':
if self.origin and 'Return of' in self.origin:
raise UserError("Button ini hanya untuk Logistik")
@@ -927,14 +1017,99 @@ class StockPicking(models.Model):
self.validation_minus_onhand_quantity()
self.responsible = self.env.user.id
+ # 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()
self.calculate_line_no()
self.date_done = datetime.datetime.utcnow()
+ self.driver_departure_date = datetime.datetime.utcnow()
self.state_reserve = 'done'
self.final_seq = 0
+ self.set_picking_code_out()
+ self.send_koli_to_so()
+ if not self.env.context.get('skip_koli_check'):
+ for picking in self:
+ if picking.sale_id:
+ all_koli_ids = picking.sale_id.koli_lines.filtered(lambda k: k.state != 'delivered').ids
+ scanned_koli_ids = picking.scan_koli_lines.mapped('koli_id.id')
+
+ missing_koli_ids = set(all_koli_ids) - set(scanned_koli_ids)
+
+ if len(missing_koli_ids) > 0 and picking.picking_type_code == 'outgoing' and 'BU/OUT/' in picking.name:
+ missing_koli_names = picking.sale_id.koli_lines.filtered(lambda k: k.id in missing_koli_ids and k.state != 'delivered').mapped('display_name')
+ missing_koli_list = "\n".join(f"- {name}" for name in missing_koli_names)
+
+ # Buat wizard modal warning
+ wizard = self.env['warning.modal.wizard'].create({
+ 'message': f"Berikut Koli yang belum discan:\n{missing_koli_list}",
+ 'picking_id': picking.id,
+ })
+
+ return {
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'warning.modal.wizard',
+ 'view_mode': 'form',
+ 'res_id': wizard.id,
+ 'target': 'new',
+ }
self.send_mail_bills()
return res
+
+ def set_picking_code_out(self):
+ for picking in self:
+ # Check if picking meets criteria
+ is_bu_pick = picking.picking_type_code == 'internal' and 'BU/PICK/' in picking.name
+ if not is_bu_pick:
+ continue
+
+ # Find matching outgoing transfers
+ bu_out_transfers = self.search([
+ ('name', 'like', 'BU/OUT/%'),
+ ('sale_id', '=', picking.sale_id.id),
+ ('picking_type_code', '=', 'outgoing'),
+ ('picking_code', '=', False),
+ ('state', 'not in', ['done', 'cancel'])
+ ])
+
+ # Assign sequence code to each matching transfer
+ for transfer in bu_out_transfers:
+ transfer.picking_code = self.env['ir.sequence'].next_by_code('stock.picking.code')
+
+ def check_koli(self):
+ for picking in self:
+ sale_id = picking.sale_id
+ for koli_lines in picking.scan_koli_lines:
+ if koli_lines.koli_id.sale_order_id != sale_id:
+ raise UserError('Koli tidak sesuai')
+
+ def send_koli_to_so(self):
+ for picking in self:
+ 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:
+ if picking.state == 'done':
+ 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:
@@ -1015,25 +1190,32 @@ class StockPicking(models.Model):
res = super(StockPicking, self).action_cancel()
return res
-
@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)
+ records = super(StockPicking, self).create(vals)
+
+ # Panggil sync_sale_line setelah record dibuat
+ # records.sync_sale_line(vals)
+ return records
+
+ def sync_sale_line(self, vals):
+ # Pastikan kita bekerja dengan record yang sudah ada
+ for picking in self:
+ if picking.picking_type_code == 'internal' and 'BU/PICK/' in picking.name:
+ for line in picking.move_ids_without_package:
+ if line.product_id and picking.sale_id:
+ sale_line = self.env['sale.order.line'].search([
+ ('product_id', '=', line.product_id.id),
+ ('order_id', '=', picking.sale_id.id)
+ ], limit=1) # Tambahkan limit=1 untuk efisiensi
+
+ if sale_line:
+ line.sale_line_id = sale_line.id
def write(self, vals):
self._use_faktur(vals)
+ self.sync_sale_line(vals)
for picking in self:
# Periksa apakah kondisi terpenuhi saat data diubah
if (vals.get('picking_type_code', picking.picking_type_code) == 'incoming' and
@@ -1454,4 +1636,245 @@ class BarcodeProduct(models.Model):
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
+ raise UserError('Barcode sudah terisi')
+
+class CheckKoli(models.Model):
+ _name = 'check.koli'
+ _description = 'Check Koli'
+ _order = 'picking_id, id'
+ _rec_name = 'koli'
+
+ picking_id = fields.Many2one(
+ 'stock.picking',
+ string='Picking Reference',
+ required=True,
+ ondelete='cascade',
+ index=True,
+ copy=False,
+ )
+ koli = fields.Char(string='Koli')
+ reserved_id = fields.Many2one('stock.picking', string='Reserved Picking')
+ check_koli_progress = fields.Char(
+ string="Progress Check Koli"
+ )
+
+ @api.constrains('koli')
+ def _check_koli_progress(self):
+ for check in self:
+ if check.picking_id:
+ all_checks = self.env['check.koli'].search([('picking_id', '=', check.picking_id.id)], order='id')
+ if all_checks:
+ check_index = list(all_checks).index(check) + 1 # Nomor urut check
+ total_so_koli = len(all_checks)
+ check.check_koli_progress = f"{check_index}/{total_so_koli}" if total_so_koli else "0/0"
+
+class ScanKoli(models.Model):
+ _name = 'scan.koli'
+ _description = 'Scan Koli'
+ _order = 'picking_id, id'
+ _rec_name = 'koli_id'
+
+ picking_id = fields.Many2one(
+ 'stock.picking',
+ string='Picking Reference',
+ required=True,
+ ondelete='cascade',
+ index=True,
+ copy=False,
+ )
+ koli_id = fields.Many2one('sales.order.koli', string='Koli')
+ scan_koli_progress = fields.Char(
+ string="Progress Scan Koli",
+ compute="_compute_scan_koli_progress"
+ )
+
+ def _compute_scan_koli_progress(self):
+ for scan in self:
+ if scan.picking_id:
+ all_scans = self.env['scan.koli'].search([('picking_id', '=', scan.picking_id.id)], order='id')
+ if all_scans:
+ scan_index = list(all_scans).index(scan) + 1 # Nomor urut scan
+ 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.onchange('koli_id')
+ def _onchange_koli_compare_with_konfirm_koli(self):
+ if not self.koli_id:
+ return
+
+ if not self.picking_id.konfirm_koli_lines:
+ raise UserError(_('Mapping Koli Harus Diisi!'))
+
+ koli_picking = self.koli_id.picking_id._origin
+
+ konfirm_pick_ids = [
+ line.pick_id._origin
+ for line in self.picking_id.konfirm_koli_lines
+ if line.pick_id
+ ]
+
+ if koli_picking not in konfirm_pick_ids:
+ raise UserError(_('Koli tidak sesuai dengan mapping koli, pastikan picking terkait benar!'))
+
+ @api.constrains('picking_id', 'koli_id')
+ def _check_duplicate_koli(self):
+ for record in self:
+ if record.koli_id:
+ existing_koli = self.search([
+ ('picking_id', '=', record.picking_id.id),
+ ('koli_id', '=', record.koli_id.id),
+ ('id', '!=', record.id)
+ ])
+ if existing_koli:
+ raise ValidationError(f"⚠️ Koli '{record.koli_id.display_name}' sudah discan untuk picking ini!")
+
+ def unlink(self):
+ picking_ids = set(self.mapped('koli_id.picking_id.id'))
+ for scan in self:
+ koli = scan.koli_id.koli_id
+ if koli:
+ koli.reserved_id = False
+
+ for picking_id in picking_ids:
+ remaining_scans = self.env['sales.order.koli'].search_count([
+ ('koli_id.picking_id', '=', picking_id)
+ ])
+
+ delete_koli = len(self.filtered(lambda rec: rec.koli_id.picking_id.id == picking_id))
+
+ if remaining_scans == delete_koli:
+ picking = self.env['stock.picking'].browse(picking_id)
+ picking.linked_out_picking_id = False
+ else:
+ raise UserError(_("Tidak dapat menghapus scan koli, karena masih ada scan koli lain yang tersisa untuk picking ini."))
+
+ for picking_id in picking_ids:
+ self._reset_qty_done_if_no_scan(picking_id)
+
+ # self.check_koli_not_balance()
+
+ 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):
+ for scan in self:
+ if scan.picking_id:
+ all_scans = self.env['scan.koli'].search([('picking_id', '=', scan.picking_id.id)], order='id')
+ if all_scans:
+ scan_index = list(all_scans).index(scan) + 1 # Nomor urut scan
+ 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.constrains('picking_id', 'picking_id.total_so_koli')
+ def _check_koli_validation(self):
+ for scan in self.picking_id.scan_koli_lines:
+ 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(self.picking_id.scan_koli_lines)
+ if total_scans != self.picking_id.total_so_koli:
+ raise UserError(_("Jumlah scan koli tidak sama dengan total SO koli!"))
+
+ # def check_koli_not_balance(self):
+ # for scan in self:
+ # total_scancs = self.env['scan.koli'].search_count([('picking_id', '=', scan.picking_id.id), ('id', '!=', scan.id)])
+ # if total_scancs != scan.picking_id.total_so_koli:
+ # raise UserError(_("Jumlah scan koli tidak sama dengan total SO koli!"))
+
+ @api.onchange('koli_id')
+ def _onchange_koli_id(self):
+ if not self.koli_id:
+ return
+
+ 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.constrains('koli_id')
+ def _send_product_from_koli_id(self):
+ if not self.koli_id:
+ return
+
+ koli_count_by_picking = defaultdict(int)
+ for scan in self:
+ koli_count_by_picking[scan.koli_id.picking_id.id] += 1
+
+ for picking_id, total_koli in koli_count_by_picking.items():
+ picking = self.env['stock.picking'].browse(picking_id)
+
+ if total_koli == picking.quantity_koli:
+ pick_moves = self.env['stock.move.line'].search([('picking_id', '=', picking_id)])
+ out_moves = self.env['stock.move.line'].search([('picking_id', '=', picking.linked_out_picking_id.id)])
+
+ for pick_move in pick_moves:
+ corresponding_out_move = out_moves.filtered(lambda m: m.product_id == pick_move.product_id)
+ if corresponding_out_move:
+ corresponding_out_move.qty_done += pick_move.qty_done
+
+ def _reset_qty_done_if_no_scan(self, picking_id):
+ product_bu_pick = self.env['stock.move.line'].search([('picking_id', '=', picking_id)])
+
+ for move in product_bu_pick:
+ product_bu_out = self.env['stock.move.line'].search([('picking_id', '=', self.picking_id.id), ('product_id', '=', move.product_id.id)])
+ for bu_out in product_bu_out:
+ bu_out.qty_done -= move.qty_done
+ # if remaining_scans == 0:
+ # picking = self.env['stock.picking'].browse(picking_id)
+ # picking.move_line_ids_without_package.write({'qty_done': 0})
+ # picking.message_post(body=f"⚠ qty_done direset ke 0 untuk Picking {picking.name} karena tidak ada scan.koli yang tersisa.")
+
+ # return remaining_scans
+
+class KonfirmKoli(models.Model):
+ _name = 'konfirm.koli'
+ _description = 'Konfirm Koli'
+ _order = 'picking_id, id'
+ _rec_name = 'pick_id'
+
+ picking_id = fields.Many2one(
+ 'stock.picking',
+ string='Picking Reference',
+ required=True,
+ ondelete='cascade',
+ index=True,
+ copy=False,
+ )
+ pick_id = fields.Many2one('stock.picking', string='Pick')
+
+ @api.constrains('pick_id')
+ def _check_duplicate_pick_id(self):
+ for rec in self:
+ exist = self.search([
+ ('pick_id', '=', rec.pick_id.id),
+ ('picking_id', '=', rec.picking_id.id),
+ ('id', '!=', rec.id),
+ ])
+
+ if exist:
+ raise UserError(f"⚠️ '{rec.pick_id.display_name}' sudah discan untuk picking ini!")
+
+class WarningModalWizard(models.TransientModel):
+ _name = 'warning.modal.wizard'
+ _description = 'Peringatan Koli Belum Diperiksa'
+
+ name = fields.Char(default="⚠️ Perhatian!")
+ message = fields.Text()
+ picking_id = fields.Many2one('stock.picking')
+
+ def action_continue(self):
+ if self.picking_id:
+ return self.picking_id.with_context(skip_koli_check=True).button_validate()
+ return {'type': 'ir.actions.act_window_close'}
+
+