summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indoteknik_custom/models/stock_picking.py505
1 files changed, 279 insertions, 226 deletions
diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py
index 1291737e..ce1399fe 100644
--- a/indoteknik_custom/models/stock_picking.py
+++ b/indoteknik_custom/models/stock_picking.py
@@ -15,22 +15,26 @@ import requests
import time
import logging
import re
+
_logger = logging.getLogger(__name__)
_biteship_url = "https://api.biteship.com/v1"
_biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA"
+
+
# _biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo"
-
-
+
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, copy=False)
+ konfirm_koli_lines = fields.One2many('konfirm.koli', 'picking_id', string='Konfirm Koli', auto_join=True,
+ copy=False)
scan_koli_lines = fields.One2many('scan.koli', 'picking_id', string='Scan Koli', auto_join=True, copy=False)
check_koli_lines = fields.One2many('check.koli', 'picking_id', string='Check Koli', auto_join=True, copy=False)
-
- check_product_lines = fields.One2many('check.product', 'picking_id', string='Check Product', auto_join=True, copy=False)
+
+ check_product_lines = fields.One2many('check.product', 'picking_id', string='Check Product', auto_join=True,
+ copy=False)
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')
@@ -89,18 +93,23 @@ class StockPicking(models.Model):
approval_status = fields.Selection([
('pengajuan1', 'Approval Accounting'),
('approved', 'Approved'),
- ], string='Approval Status', readonly=True, copy=False, index=True, tracking=3, help="Approval Status untuk Internal Use")
+ ], string='Approval Status', readonly=True, copy=False, index=True, tracking=3,
+ help="Approval Status untuk Internal Use")
approval_receipt_status = fields.Selection([
('pengajuan1', 'Approval Logistic'),
('approved', 'Approved'),
- ], string='Approval Receipt Status', readonly=True, copy=False, index=True, tracking=3, help="Approval Status untuk Receipt")
+ ], string='Approval Receipt Status', readonly=True, copy=False, index=True, tracking=3,
+ help="Approval Status untuk Receipt")
approval_return_status = fields.Selection([
('pengajuan1', 'Approval Finance'),
('approved', 'Approved'),
- ], string='Approval Return Status', readonly=True, copy=False, index=True, tracking=3, help="Approval Status untuk Return")
- date_doc_kirim = fields.Datetime(string='Tanggal Kirim di SJ', help="Tanggal Kirim di cetakan SJ, tidak berpengaruh ke Accounting", tracking=True, copy=False)
+ ], string='Approval Return Status', readonly=True, copy=False, index=True, tracking=3,
+ help="Approval Status untuk Return")
+ date_doc_kirim = fields.Datetime(string='Tanggal Kirim di SJ',
+ help="Tanggal Kirim di cetakan SJ, tidak berpengaruh ke Accounting", tracking=True,
+ copy=False)
note_logistic = fields.Selection([
('wait_so_together', 'Tunggu SO Barengan'),
('not_paid', 'Customer belum bayar'),
@@ -110,7 +119,8 @@ class StockPicking(models.Model):
('expedition_closed', 'Eskpedisi belum buka')
], string='Note Logistic', help='jika field ini diisi maka tidak akan dihitung ke lead time')
waybill_id = fields.One2many(comodel_name='airway.bill', inverse_name='do_id', string='Airway Bill')
- purchase_representative_id = fields.Many2one('res.users', related='move_lines.purchase_line_id.order_id.user_id', string="Purchase Representative")
+ purchase_representative_id = fields.Many2one('res.users', related='move_lines.purchase_line_id.order_id.user_id',
+ string="Purchase Representative")
carrier_id = fields.Many2one('delivery.carrier', string='Shipping Method')
shipping_status = fields.Char(string='Shipping Status', compute="_compute_shipping_status")
date_reserved = fields.Datetime(string="Date Reserved", help='Tanggal ter-reserved semua barang nya', copy=False)
@@ -131,9 +141,9 @@ class StockPicking(models.Model):
('invoiced', 'Fully Invoiced'),
('to invoice', 'To Invoice'),
('no', 'Nothing to Invoice')
- ], string='Invoice Status', related="sale_id.invoice_status")
+ ], string='Invoice Status', related="sale_id.invoice_status")
note_return = fields.Text(string="Note Return", help="Catatan untuk kirim barang kembali")
-
+
state_reserve = fields.Selection([
('waiting', 'Waiting For Fullfilment'),
('ready', 'Ready to Ship'),
@@ -146,7 +156,8 @@ class StockPicking(models.Model):
('waiting', 'Waiting For Approve by MD'),
('pending', 'Pending (perlu koordinasi dengan MD)'),
('done', 'Approve by MD'),
- ], string='Approval MD Gudang Selisih', tracking=True, copy=False, help="The current state of the MD Approval transfer barang from gudang selisih.")
+ ], string='Approval MD Gudang Selisih', tracking=True, copy=False,
+ help="The current state of the MD Approval transfer barang from gudang selisih.")
# show_state_approve_md = fields.Boolean(compute="_compute_show_state_approve_md")
# def _compute_show_state_approve_md(self):
@@ -158,13 +169,13 @@ class StockPicking(models.Model):
linked_manual_bu_out = fields.Many2one('stock.picking', string='BU Out', copy=False)
area_name = fields.Char(string="Area", compute="_compute_area_name")
+
@api.depends('real_shipping_id.kecamatan_id', 'real_shipping_id.kota_id')
def _compute_area_name(self):
for record in self:
district = record.real_shipping_id.kecamatan_id.name or ''
city = record.real_shipping_id.kota_id.name or ''
record.area_name = f"{district}, {city}".strip(', ')
-
# def write(self, vals):
# if 'linked_manual_bu_out' in vals:
@@ -189,7 +200,7 @@ class StockPicking(models.Model):
# picking = self.env['stock.picking'].browse(vals['linked_manual_bu_out'])
# picking.state_packing = 'packing_done'
# return record
-
+
@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:
@@ -209,8 +220,10 @@ class StockPicking(models.Model):
for picking in self:
picking.dokumen_pengiriman = picking.partner_id.dokumen_pengiriman_input
- dokumen_tanda_terima = fields.Char(string='Dokumen Tanda Terima yang Diberikan Pada Saat Pengiriman Barang', readonly=True, compute=_compute_dokumen_tanda_terima)
- dokumen_pengiriman = fields.Char(string='Dokumen yang Dibawa Saat Pengiriman Barang', readonly=True, compute=_compute_dokumen_pengiriman)
+ dokumen_tanda_terima = fields.Char(string='Dokumen Tanda Terima yang Diberikan Pada Saat Pengiriman Barang',
+ readonly=True, compute=_compute_dokumen_tanda_terima)
+ dokumen_pengiriman = fields.Char(string='Dokumen yang Dibawa Saat Pengiriman Barang', readonly=True,
+ compute=_compute_dokumen_pengiriman)
# Envio Tracking Section
envio_id = fields.Char(string="Envio ID", readonly=True)
@@ -255,8 +268,10 @@ class StockPicking(models.Model):
# countdown_hours = fields.Float(string='Countdown in Hours', compute='_callculate_sequance', default=False, store=False, compute_sudo=False)
# countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_callculate_sequance', store=False, compute_sudo=False)
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')
+ 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')
approval_invoice_date_id = fields.Many2one('approval.invoice.date', string='Approval Invoice Date')
last_update_date_doc_kirim = fields.Datetime(string='Last Update Tanggal Kirim')
update_date_doc_kirim_add = fields.Boolean(string='Update Tanggal Kirim Lewat ADD')
@@ -266,10 +281,10 @@ class StockPicking(models.Model):
if record.last_update_date_doc_kirim and not self.env.context.get('from_button_approve'):
kirim_date = fields.Datetime.from_string(record.last_update_date_doc_kirim)
now = fields.Datetime.now()
-
+
deadline = kirim_date + timedelta(days=1)
deadline = deadline.replace(hour=10, minute=0, second=0)
-
+
if now > deadline:
raise ValidationError(
_("Anda tidak dapat mengubah Tanggal Kirim setelah jam 10:00 pada hari berikutnya!")
@@ -281,12 +296,15 @@ class StockPicking(models.Model):
rec.calculate_line_no()
if rec.picking_type_code == 'outgoing' and 'BU/OUT/' in rec.name and rec.partner_id.id != 96868:
- invoice = self.env['account.move'].search([('sale_id', '=', rec.sale_id.id), ('move_type', '=', 'out_invoice'), ('state', '=', 'posted')], limit=1, order='create_date desc')
+ invoice = self.env['account.move'].search(
+ [('sale_id', '=', rec.sale_id.id), ('move_type', '=', 'out_invoice'), ('state', '=', 'posted')],
+ limit=1, order='create_date desc')
if invoice and not self.env.context.get('active_model') == 'stock.picking':
rec._check_date_doc_kirim_modification()
if rec.date_doc_kirim != invoice.invoice_date and not self.env.context.get('from_button_approve'):
- get_approval_invoice_date = self.env['approval.invoice.date'].search([('picking_id', '=', rec.id),('state', '=', 'draft')], limit=1)
+ get_approval_invoice_date = self.env['approval.invoice.date'].search(
+ [('picking_id', '=', rec.id), ('state', '=', 'draft')], limit=1)
if get_approval_invoice_date and get_approval_invoice_date.state == 'draft':
get_approval_invoice_date.date_doc_do = rec.date_doc_kirim
@@ -306,12 +324,13 @@ class StockPicking(models.Model):
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
- 'params': { 'title': 'Notification', 'message': 'Invoice Date Tidak Sesuai, Document Approval Invoice Date Terbuat', 'next': {'type': 'ir.actions.act_window_close'} },
+ 'params': {'title': 'Notification',
+ 'message': 'Invoice Date Tidak Sesuai, Document Approval Invoice Date Terbuat',
+ 'next': {'type': 'ir.actions.act_window_close'}},
}
-
+
rec.last_update_date_doc_kirim = datetime.datetime.utcnow()
-
-
+
@api.constrains('scan_koli_lines')
def _constrains_scan_koli_lines(self):
now = datetime.datetime.utcnow()
@@ -319,16 +338,18 @@ class StockPicking(models.Model):
if len(picking.scan_koli_lines) > 0:
if len(picking.scan_koli_lines) != picking.total_mapping_koli:
raise UserError("Scan Koli Tidak Sesuai Dengan Total Mapping Koli")
-
+
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')])
+ 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')])
+ 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):
@@ -356,21 +377,21 @@ class StockPicking(models.Model):
'koli_id': rec.id,
})
else:
- raise UserError('Tidak Bisa Mengubah Quantity Koli Karena Koli Dari Picking Ini Sudah Dipakai Di BU/OUT!')
+ 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)}",
+ '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();"
self.env.cr.execute(query)
-
-
+
# @api.depends('estimated_ready_ship_date', 'state')
# def _callculate_sequance(self):
# for record in self:
@@ -379,9 +400,9 @@ class StockPicking(models.Model):
# rts = record.estimated_ready_ship_date - waktu.now()
# rts_days = rts.days
# rts_hours = divmod(rts.seconds, 3600)
-
+
# estimated_by_erts = rts.total_seconds() / 3600
-
+
# record.countdown_ready_to_ship = f"{rts_days} days, {rts_hours} hours"
# record.countdown_hours = estimated_by_erts
# else:
@@ -391,7 +412,6 @@ class StockPicking(models.Model):
# _logger.error(f"Error calculating sequance {record.id}: {str(e)}")
# print(str(e))
# return { 'error': str(e) }
-
# @api.depends('estimated_ready_ship_date', 'state')
# def _compute_countdown_hours(self):
@@ -431,7 +451,7 @@ class StockPicking(models.Model):
('state', '=', 'done'),
('carrier_id', '=', 9),
('lalamove_order_id', '!=', False)
- ])
+ ])
for picking in pickings:
try:
order_id = picking.lalamove_order_id
@@ -466,7 +486,7 @@ class StockPicking(models.Model):
for stop in stops:
pod = stop.get("POD", {})
if pod.get("status") == "DELIVERED":
- image_url = pod.get("image") # Sesuaikan jika key berbeda
+ image_url = pod.get("image") # Sesuaikan jika key berbeda
self.lalamove_image_url = image_url
address = stop.get("address")
@@ -487,7 +507,6 @@ class StockPicking(models.Model):
else:
raise UserError(f"Error {response.status_code}: {response.text}")
-
def _convert_to_wib(self, date_str):
"""
Mengonversi string waktu ISO 8601 ke format waktu Indonesia (WIB)
@@ -573,7 +592,7 @@ class StockPicking(models.Model):
picking.tracking_by = self.env.user.id
ata_at_str = data.get("ata_at")
envio_ata = self._convert_to_datetime(data.get("ata_at"))
-
+
picking.driver_arrival_date = envio_ata
if data.get("status") != 'delivered':
picking.driver_arrival_date = False
@@ -584,13 +603,13 @@ class StockPicking(models.Model):
raise UserError(f"Kesalahan tidak terduga: {str(e)}")
def action_send_to_biteship(self):
-
+
if self.biteship_tracking_id:
raise UserError(f"Order ini sudah dikirim ke Biteship. Dengan Tracking Id: {self.biteship_tracking_id}")
-
+
# Mencari data sale.order.line berdasarkan sale_id
products = self.env['sale.order.line'].search([('order_id', '=', self.sale_id.id)])
-
+
# Fungsi untuk membangun items_data dari order lines
def build_items_data(lines):
return [{
@@ -612,7 +631,7 @@ class StockPicking(models.Model):
('order_id', '=', self.sale_id.id),
('product_id', '=', move_line.product_id.id)
], limit=1)
-
+
if order_line:
items_data_instant.append({
"name": order_line.product_id.name,
@@ -623,7 +642,7 @@ class StockPicking(models.Model):
})
payload = {
- "reference_id " : self.sale_id.name,
+ "reference_id ": self.sale_id.name,
"shipper_contact_name": self.carrier_id.pic_name or '',
"shipper_contact_phone": self.carrier_id.pic_phone or '',
"shipper_organization": self.carrier_id.name,
@@ -644,19 +663,20 @@ class StockPicking(models.Model):
}
# Cek jika pengiriman instant atau same_day
- if self.sale_id.delivery_service_type and ("instant" in self.sale_id.delivery_service_type or "same_day" in self.sale_id.delivery_service_type):
+ if self.sale_id.delivery_service_type and (
+ "instant" in self.sale_id.delivery_service_type or "same_day" in self.sale_id.delivery_service_type):
payload.update({
- "origin_coordinate" :{
+ "origin_coordinate": {
"latitude": -6.3031123,
- "longitude" : 106.7794934999
+ "longitude": 106.7794934999
},
- "destination_coordinate" : {
+ "destination_coordinate": {
"latitude": self.real_shipping_id.latitude,
"longitude": self.real_shipping_id.longtitude,
},
"items": items_data_instant
})
-
+
api_key = _biteship_api_key
headers = {
"Authorization": f"Bearer {api_key}",
@@ -664,7 +684,7 @@ class StockPicking(models.Model):
}
# Kirim request ke Biteship
- response = requests.post(_biteship_url+'/orders', headers=headers, json=payload)
+ response = requests.post(_biteship_url + '/orders', headers=headers, json=payload)
if response.status_code == 200:
data = response.json()
@@ -673,7 +693,7 @@ class StockPicking(models.Model):
self.biteship_tracking_id = data.get("courier", {}).get("tracking_id", "")
self.biteship_waybill_id = data.get("courier", {}).get("waybill_id", "")
self.delivery_tracking_no = data.get("courier", {}).get("waybill_id", "")
-
+
waybill_id = data.get("courier", {}).get("waybill_id", "")
message = f"✅ Berhasil Order ke Biteship! Resi: {waybill_id}" if waybill_id else "⚠️ Order berhasil, tetapi tidak ada nomor resi."
@@ -690,10 +710,10 @@ class StockPicking(models.Model):
error_message = error_data.get("error", "Unknown error")
error_code = error_data.get("code", "No code provided")
raise UserError(f"Error saat mengirim ke Biteship: {error_message} (Code: {error_code})")
-
+
@api.constrains('driver_departure_date')
- def constrains_driver_departure_date(self):
- if not self.date_doc_kirim:
+ def constrains_driver_departure_date(self):
+ if not self.date_doc_kirim:
self.date_doc_kirim = self.driver_departure_date
@api.constrains('arrival_time')
@@ -721,32 +741,32 @@ class StockPicking(models.Model):
if not self._context.get('darimana') == 'sale.order' and self.env.user.id not in users_in_group.mapped('id'):
self.sale_id.unreserve_id = self.id
return self._create_approval_notification('Logistic')
-
+
res = super(StockPicking, self).do_unreserve()
current_time = datetime.datetime.utcnow()
self.date_unreserve = current_time
-
+
return res
-
+
def check_state_reserve(self):
pickings = self.search([
('state', 'not in', ['cancel', 'draft', 'done']),
('picking_type_code', '=', 'internal'),
('name', 'ilike', 'BU/PICK/'),
])
-
+
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 = ''
-
+
self.check_state_reserve_backorder()
def check_state_reserve_backorder(self):
@@ -756,29 +776,29 @@ class StockPicking(models.Model):
('picking_type_code', '=', 'internal'),
('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 = ''
-
+
def _create_approval_notification(self, approval_role):
title = 'Warning'
message = f'Butuh approval sales untuk unreserved'
return self._create_notification_action(title, message)
-
+
def _create_notification_action(self, title, message):
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
- 'params': { 'title': title, 'message': message, 'next': {'type': 'ir.actions.act_window_close'} },
+ 'params': {'title': title, 'message': message, 'next': {'type': 'ir.actions.act_window_close'}},
}
def _compute_shipping_status(self):
@@ -788,7 +808,7 @@ class StockPicking(models.Model):
status = 'shipment'
elif rec.driver_departure_date and (rec.sj_return_date or rec.driver_arrival_date):
status = 'completed'
-
+
rec.shipping_status = status
def action_create_invoice_from_mr(self):
@@ -796,10 +816,10 @@ class StockPicking(models.Model):
"""
if not self.env.user.is_accounting:
raise UserError('Hanya Accounting yang bisa membuat Bill')
-
+
precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
- #custom here
+ # custom here
po = self.env['purchase.order'].search([
('name', '=', self.group_id.name)
])
@@ -816,24 +836,29 @@ class StockPicking(models.Model):
invoice_vals = order._prepare_invoice()
# Invoice line values (keep only necessary sections).
for line in self.move_ids_without_package:
- po_line = self.env['purchase.order.line'].search([('order_id', '=', po.id), ('product_id', '=', line.product_id.id)], limit=1)
+ po_line = self.env['purchase.order.line'].search(
+ [('order_id', '=', po.id), ('product_id', '=', line.product_id.id)], limit=1)
qty = line.product_uom_qty
if po_line.display_type == 'line_section':
pending_section = line
continue
if not float_is_zero(po_line.qty_to_invoice, precision_digits=precision):
if pending_section:
- invoice_vals['invoice_line_ids'].append((0, 0, pending_section._prepare_account_move_line_from_mr(po_line, qty)))
+ invoice_vals['invoice_line_ids'].append(
+ (0, 0, pending_section._prepare_account_move_line_from_mr(po_line, qty)))
pending_section = None
- invoice_vals['invoice_line_ids'].append((0, 0, line._prepare_account_move_line_from_mr(po_line, qty)))
+ invoice_vals['invoice_line_ids'].append(
+ (0, 0, line._prepare_account_move_line_from_mr(po_line, qty)))
invoice_vals_list.append(invoice_vals)
if not invoice_vals_list:
- raise UserError(_('There is no invoiceable line. If a product has a control policy based on received quantity, please make sure that a quantity has been received.'))
+ raise UserError(
+ _('There is no invoiceable line. If a product has a control policy based on received quantity, please make sure that a quantity has been received.'))
# 2) group by (company_id, partner_id, currency_id) for batch creation
new_invoice_vals_list = []
- for grouping_keys, invoices in groupby(invoice_vals_list, key=lambda x: (x.get('company_id'), x.get('partner_id'), x.get('currency_id'))):
+ for grouping_keys, invoices in groupby(invoice_vals_list, key=lambda x: (
+ x.get('company_id'), x.get('partner_id'), x.get('currency_id'))):
origins = set()
payment_refs = set()
refs = set()
@@ -863,7 +888,8 @@ class StockPicking(models.Model):
# 4) Some moves might actually be refunds: convert them if the total amount is negative
# We do this after the moves have been created since we need taxes, etc. to know if the total
# is actually negative or not
- moves.filtered(lambda m: m.currency_id.round(m.amount_total) < 0).action_switch_invoice_into_refund_credit_note()
+ moves.filtered(
+ lambda m: m.currency_id.round(m.amount_total) < 0).action_switch_invoice_into_refund_credit_note()
return self.action_view_invoice_from_mr(moves)
@@ -926,7 +952,7 @@ class StockPicking(models.Model):
# for stock_move_line in stock_move_lines:
# if stock_move_line.picking_id.state not in list_state:
# continue
- # raise UserError('Sudah pernah dikirim kalender')
+ # raise UserError('Sudah pernah dikirim kalender')
for pick in self:
if not pick.is_internal_use:
@@ -952,18 +978,20 @@ class StockPicking(models.Model):
if self.picking_type_code == 'outgoing':
if self.env.user.id in [3988, 3401, 20] or (
- self.env.user.has_group('indoteknik_custom.group_role_purchasing') and 'Return of' in self.origin
+ self.env.user.has_group(
+ 'indoteknik_custom.group_role_purchasing') and 'Return of' in self.origin
):
action['context'] = {'picking_ids': [x.id for x in self]}
return action
- elif not self.env.user.has_group('indoteknik_custom.group_role_purchasing') and 'Return of' in self.origin:
+ elif not self.env.user.has_group(
+ 'indoteknik_custom.group_role_purchasing') and 'Return of' in self.origin:
raise UserError('Harus Purchasing yang Ask Return')
else:
raise UserError('Harus Sales Admin yang Ask Return')
elif self.picking_type_code == 'incoming':
if self.env.user.has_group('indoteknik_custom.group_role_purchasing') or (
- self.env.user.id in [3988, 3401, 20] and 'Return of' in self.origin
+ self.env.user.id in [3988, 3401, 20] and 'Return of' in self.origin
):
action['context'] = {'picking_ids': [x.id for x in self]}
return action
@@ -973,7 +1001,7 @@ class StockPicking(models.Model):
raise UserError('Harus Purchasing yang Ask Return')
def calculate_line_no(self):
-
+
for picking in self:
name = picking.group_id.name
for move in picking.move_ids_without_package:
@@ -996,10 +1024,10 @@ class StockPicking(models.Model):
def _compute_summary_qty(self):
for picking in self:
sum_qty_detail = sum_qty_operation = count_line_detail = count_line_operation = 0
- for detail in picking.move_line_ids_without_package: # detailed operations
+ for detail in picking.move_line_ids_without_package: # detailed operations
sum_qty_detail += detail.qty_done
count_line_detail += 1
- for operation in picking.move_ids_without_package: # operations
+ for operation in picking.move_ids_without_package: # operations
sum_qty_operation += operation.product_uom_qty
count_line_operation += 1
picking.summary_qty_detail = sum_qty_detail
@@ -1021,13 +1049,13 @@ class StockPicking(models.Model):
])
if (
- self.picking_type_id.id == 29
- and quant
- and line.location_id.id == bu_location_id
- and quant.inventory_quantity < line.product_uom_qty
+ self.picking_type_id.id == 29
+ and quant
+ and line.location_id.id == bu_location_id
+ and quant.inventory_quantity < line.product_uom_qty
):
raise UserError('Quantity reserved lebih besar dari quantity onhand di product')
-
+
def check_qty_done_stock(self):
for line in self.move_line_ids_without_package:
def check_qty_per_inventory(self, product, location):
@@ -1040,10 +1068,11 @@ class StockPicking(models.Model):
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 button_state_approve_md(self):
group_id = self.env.ref('indoteknik_custom.group_role_merchandiser').id
users_in_group = self.env['res.users'].search([('groups_id', 'in', [group_id])])
@@ -1068,7 +1097,8 @@ class StockPicking(models.Model):
group_id = self.env.ref('indoteknik_custom.group_role_merchandiser').id
users_in_group = self.env['res.users'].search([('groups_id', 'in', [group_id])])
active_model = self.env.context.get('active_model')
- if self.location_id.id == 47 and self.env.user.id not in users_in_group.mapped('id') and self.state_approve_md != 'done':
+ if self.location_id.id == 47 and self.env.user.id not in users_in_group.mapped(
+ 'id') and self.state_approve_md != 'done':
self.state_approve_md = 'waiting' if self.state_approve_md != 'pending' else 'pending'
self.env.cr.commit()
raise UserError("Transfer dari gudang selisih harus di approve MD, Hubungi MD agar bisa di Validate")
@@ -1076,37 +1106,36 @@ 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'
-
- 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):
+ 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):
+ 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 not self.linked_manual_bu_out and 'BU/PICK/' in self.name:
raise UserError(_("Isi BU Out terlebih dahulu!"))
-
+
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))
-
+ 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")
@@ -1128,7 +1157,7 @@ class StockPicking(models.Model):
if self.is_internal_use and not self.env.user.is_accounting:
raise UserError("Harus di Approve oleh Accounting")
-
+
if self.picking_type_id.id == 28 and not self.env.user.is_logistic_approver:
raise UserError("Harus di Approve oleh Logistik")
@@ -1163,6 +1192,16 @@ class StockPicking(models.Model):
self.final_seq = 0
self.set_picking_code_out()
self.send_koli_to_so()
+
+ if (self.state_reserve == 'done' and self.picking_type_code == 'internal' and 'BU/PICK/' in self.name
+ and self.linked_manual_bu_out):
+ if not self.linked_manual_bu_out.date_reserved:
+ current_datetime = datetime.datetime.utcnow()
+ self.linked_manual_bu_out.date_reserved = current_datetime
+ self.linked_manual_bu_out.message_post(
+ body=f"Date Reserved diisi secara otomatis dari validasi BU/PICK {self.name}"
+ )
+
if not self.env.context.get('skip_koli_check'):
for picking in self:
if picking.sale_id:
@@ -1172,7 +1211,8 @@ class StockPicking(models.Model):
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_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
@@ -1190,36 +1230,37 @@ class StockPicking(models.Model):
}
self.send_mail_bills()
return res
-
+
def check_invoice_date(self):
for picking in self:
if picking.picking_type_code != 'outgoing' or 'BU/OUT/' not in picking.name or picking.partner_id.id == 96868:
continue
-
- invoice = self.env['account.move'].search([('sale_id', '=', picking.sale_id.id), ('state','not in',['draft','cancel'])], limit=1)
-
+
+ invoice = self.env['account.move'].search(
+ [('sale_id', '=', picking.sale_id.id), ('state', 'not in', ['draft', 'cancel'])], limit=1)
+
if not invoice:
continue
-
+
if not picking.so_lama and (not picking.date_doc_kirim or not invoice.invoice_date):
raise UserError("Tanggal Kirim atau Tanggal Invoice belum diisi!")
-
+
picking_date = fields.Date.to_date(picking.date_doc_kirim)
invoice_date = fields.Date.to_date(invoice.invoice_date)
-
+
if picking_date != invoice_date and picking.update_date_doc_kirim_add:
raise UserError("Tanggal Kirim (%s) tidak sesuai dengan Tanggal Invoice (%s)!" % (
picking_date.strftime('%d-%m-%Y'),
invoice_date.strftime('%d-%m-%Y')
))
-
+
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/%'),
@@ -1228,11 +1269,10 @@ class StockPicking(models.Model):
('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:
@@ -1240,7 +1280,7 @@ class StockPicking(models.Model):
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:
@@ -1257,7 +1297,7 @@ class StockPicking(models.Model):
'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:
@@ -1265,7 +1305,7 @@ class StockPicking(models.Model):
('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):
@@ -1280,7 +1320,7 @@ class StockPicking(models.Model):
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')
@@ -1337,11 +1377,14 @@ class StockPicking(models.Model):
return True
def action_cancel(self):
- if not self.env.user.is_logistic_approver and (self.env.context.get('active_model') == 'stock.picking' or self.env.context.get('active_model') == 'stock.picking.type'):
+ if not self.env.user.is_logistic_approver and (
+ self.env.context.get('active_model') == 'stock.picking' or self.env.context.get(
+ 'active_model') == 'stock.picking.type'):
if self.origin and 'Return of' in self.origin:
raise UserError("Button ini hanya untuk Logistik")
-
- if not self.env.user.has_group('indoteknik_custom.group_role_it') and not self.env.user.has_group('indoteknik_custom.group_role_logistic') and self.picking_type_code == 'outgoing':
+
+ if not self.env.user.has_group('indoteknik_custom.group_role_it') and not self.env.user.has_group(
+ 'indoteknik_custom.group_role_logistic') and self.picking_type_code == 'outgoing':
raise UserError("Button ini hanya untuk Logistik")
res = super(StockPicking, self).action_cancel()
@@ -1351,7 +1394,7 @@ class StockPicking(models.Model):
def create(self, vals):
self._use_faktur(vals)
records = super(StockPicking, self).create(vals)
-
+
# Panggil sync_sale_line setelah record dibuat
# records.sync_sale_line(vals)
return records
@@ -1373,8 +1416,8 @@ class StockPicking(models.Model):
def write(self, vals):
if 'linked_manual_bu_out' in vals:
for record in self:
- if (record.picking_type_code == 'internal'
- and 'BU/PICK/' in record.name):
+ if (record.picking_type_code == 'internal'
+ and 'BU/PICK/' in record.name):
# Jika menghapus referensi (nilai di-set False/None)
if record.linked_manual_bu_out and not vals['linked_manual_bu_out']:
record.linked_manual_bu_out.state_packing = 'not_packing'
@@ -1399,7 +1442,8 @@ class StockPicking(models.Model):
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:
+ 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)
@@ -1443,7 +1487,7 @@ class StockPicking(models.Model):
def get_manifests(self):
if self.waybill_id and len(self.waybill_id.manifest_ids) > 0:
return [self.create_manifest_data(x.description, x.datetime) for x in self.waybill_id.manifest_ids]
-
+
status_mapping = {
'pickup': {
'arrival': 'Sudah diambil',
@@ -1468,7 +1512,7 @@ class StockPicking(models.Model):
if not status:
return manifest_datas
-
+
if arrival_date or self.sj_return_date:
manifest_datas.append(self.create_manifest_data(status['arrival'], arrival_date))
if departure_date:
@@ -1479,14 +1523,14 @@ class StockPicking(models.Model):
def get_tracking_detail(self):
self.ensure_one()
-
+
order = self.env['sale.order'].search([('name', '=', self.sale_id.name)], limit=1)
response = {
'delivery_order': {
'name': self.name,
'carrier': self.carrier_id.name or '',
- 'service' : order.delivery_service_type or '',
+ 'service': order.delivery_service_type or '',
'receiver_name': '',
'receiver_city': ''
},
@@ -1498,74 +1542,76 @@ class StockPicking(models.Model):
'is_biteship': True if self.biteship_id else False,
'manifests': self.get_manifests()
}
-
- if self.biteship_id :
+
+ if self.biteship_id:
histori = self.get_manifest_biteship()
eta_start = order.date_order + timedelta(days=order.estimated_arrival_days_start)
eta_end = order.date_order + timedelta(days=order.estimated_arrival_days)
formatted_eta = f"{eta_start.strftime('%d %b')} - {eta_end.strftime('%d %b %Y')}"
response['eta'] = formatted_eta
- response['manifests'] = histori.get("manifests", [])
- response['delivered'] = histori.get("delivered", False) or self.sj_return_date != False or self.driver_arrival_date != False
+ response['manifests'] = histori.get("manifests", [])
+ response['delivered'] = histori.get("delivered",
+ False) or self.sj_return_date != False or self.driver_arrival_date != False
response['status'] = self._map_status_biteship(histori.get("delivered"))
-
+
return response
if not self.waybill_id or len(self.waybill_id.manifest_ids) == 0:
response['delivered'] = self.sj_return_date != False or self.driver_arrival_date != False
return response
-
+
response['delivery_order']['receiver_name'] = self.waybill_id.receiver_name
response['delivery_order']['receiver_city'] = self.waybill_id.receiver_city
response['delivery_status'] = self.waybill_id._get_history('delivery_status')
response['delivered'] = self.waybill_id.delivered
return response
-
+
def get_manifest_biteship(self):
api_key = _biteship_api_key
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
-
-
+
manifests = []
-
+
try:
- # Kirim request ke Biteship
- response = requests.get(_biteship_url+'/trackings/'+self.biteship_tracking_id, headers=headers, json=manifests)
+ # Kirim request ke Biteship
+ response = requests.get(_biteship_url + '/trackings/' + self.biteship_tracking_id, headers=headers,
+ json=manifests)
result = response.json()
description = {
- 'confirmed' : 'Indoteknik telah melakukan permintaan pick-up',
- 'allocated' : 'Kurir akan melakukan pick-up pesanan',
- 'picking_up' : 'Kurir sedang dalam perjalanan menuju lokasi pick-up',
- 'picked' : 'Pesanan sudah di pick-up kurir '+result.get("courier", {}).get("name", ""),
- 'on_hold' : 'Pesanan ditahan sementara karena masalah pengiriman',
- 'dropping_off' : 'Kurir sudah ditugaskan dan pesanan akan segera diantar ke pembeli',
- 'delivered' : 'Pesanan telah sampai dan diterima oleh '+result.get("destination", {}).get("contact_name", "")
+ 'confirmed': 'Indoteknik telah melakukan permintaan pick-up',
+ 'allocated': 'Kurir akan melakukan pick-up pesanan',
+ 'picking_up': 'Kurir sedang dalam perjalanan menuju lokasi pick-up',
+ 'picked': 'Pesanan sudah di pick-up kurir ' + result.get("courier", {}).get("name", ""),
+ 'on_hold': 'Pesanan ditahan sementara karena masalah pengiriman',
+ 'dropping_off': 'Kurir sudah ditugaskan dan pesanan akan segera diantar ke pembeli',
+ 'delivered': 'Pesanan telah sampai dan diterima oleh ' + result.get("destination", {}).get(
+ "contact_name", "")
}
- if(result.get('success') == True):
+ if (result.get('success') == True):
history = result.get("history", [])
status = result.get("status", "")
-
+
for entry in reversed(history):
manifests.append({
"status": re.sub(r'[^a-zA-Z0-9\s]', ' ', entry["status"]).lower().capitalize(),
"datetime": self._convert_to_local_time(entry["updated_at"]),
- "description": description[entry["status"]],
+ "description": description[entry["status"]],
})
-
+
return {
"manifests": manifests,
"delivered": status
}
return manifests
- except Exception as e :
+ except Exception as e:
_logger.error(f"Error fetching Biteship order for picking {self.id}: {str(e)}")
- return { 'error': str(e) }
-
+ return {'error': str(e)}
+
def _convert_to_local_time(self, iso_date):
try:
dt_with_tz = waktu.fromisoformat(iso_date)
@@ -1577,7 +1623,7 @@ class StockPicking(models.Model):
return local_dt.strftime("%Y-%m-%d %H:%M:%S")
except Exception as e:
return str(e)
-
+
def _map_status_biteship(self, status):
status_mapping = {
"confirmed": "pending",
@@ -1591,7 +1637,7 @@ class StockPicking(models.Model):
"delivered": "completed"
}
return status_mapping.get(status, "Hubungi Admin")
-
+
def generate_eta_delivery(self):
current_date = datetime.datetime.now()
prepare_days = 3
@@ -1605,7 +1651,7 @@ class StockPicking(models.Model):
fastest_eta = start_date + ead_datetime
if not self.driver_departure_date and fastest_eta < current_date:
fastest_eta = current_date + ead_datetime
-
+
longest_days = 3
longest_eta = fastest_eta + datetime.timedelta(days=longest_days)
@@ -1614,9 +1660,10 @@ class StockPicking(models.Model):
formatted_fastest_eta = fastest_eta.strftime(format_time_fastest)
formatted_longest_eta = longest_eta.strftime(format_time)
-
+
return f'{formatted_fastest_eta} - {formatted_longest_eta}'
-
+
+
class CheckProduct(models.Model):
_name = 'check.product'
_description = 'Check Product'
@@ -1639,7 +1686,7 @@ class CheckProduct(models.Model):
def _onchange_code_product(self):
if not self.code_product:
return
-
+
# Cari product berdasarkan default_code, barcode, atau barcode_box
product = self.env['product.product'].search([
'|',
@@ -1648,10 +1695,10 @@ class CheckProduct(models.Model):
('barcode', '=', self.code_product),
('barcode_box', '=', self.code_product)
], limit=1)
-
+
if not product:
raise UserError("Product tidak ditemukan")
-
+
# Jika scan barcode_box, set quantity sesuai qty_pcs_box
if product.barcode_box == self.code_product:
self.product_id = product.id
@@ -1660,7 +1707,7 @@ class CheckProduct(models.Model):
# return {
# 'warning': {
# 'title': 'Info',8994175025871
-
+
# 'message': f'Product box terdeteksi. Quantity di-set ke {product.qty_pcs_box}'
# }
# }
@@ -1673,29 +1720,29 @@ class CheckProduct(models.Model):
def unlink(self):
# Get all affected pickings before deletion
pickings = self.mapped('picking_id')
-
+
# Store product_ids that will be deleted
deleted_product_ids = self.mapped('product_id')
-
+
# Perform the deletion
result = super(CheckProduct, self).unlink()
-
+
# After deletion, update moves for affected pickings
for picking in pickings:
# For products that were completely removed (no remaining check.product lines)
remaining_product_ids = picking.check_product_lines.mapped('product_id')
removed_product_ids = deleted_product_ids - remaining_product_ids
-
+
# Set quantity_done to 0 for moves of completely removed products
moves_to_reset = picking.move_ids_without_package.filtered(
lambda move: move.product_id in removed_product_ids
)
for move in moves_to_reset:
move.quantity_done = 0.0
-
+
# Also sync remaining products in case their totals changed
self._sync_check_product_to_moves(picking)
-
+
return result
@api.depends('quantity')
@@ -1711,7 +1758,6 @@ class CheckProduct(models.Model):
else:
record.status = 'Done'
-
def create(self, vals):
# Create the record
record = super(CheckProduct, self).create(vals)
@@ -1736,7 +1782,8 @@ class CheckProduct(models.Model):
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)
+ 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)
@@ -1789,8 +1836,8 @@ class CheckProduct(models.Model):
if not moves:
raise UserError((
- "The product '%s' tidak ada di operations. "
- ) % record.product_id.display_name)
+ "The product '%s' tidak ada di operations. "
+ ) % record.product_id.display_name)
total_qty_in_moves = sum(moves.mapped('product_uom_qty'))
@@ -1808,18 +1855,19 @@ class CheckProduct(models.Model):
if total_quantity > total_qty_in_moves:
raise UserError((
- "Quantity Product '%s' sudah melebihi quantity demand."
- ) % (record.product_id.display_name))
+ "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))
+ "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'
@@ -1841,7 +1889,7 @@ class BarcodeProduct(models.Model):
if barcode_product:
raise UserError('Barcode sudah digunakan {}'.format(barcode_product.display_name))
-
+
barcode_box = self.env['product.product'].search([('barcode_box', '=', self.barcode)])
if barcode_box:
@@ -1855,7 +1903,8 @@ class BarcodeProduct(models.Model):
record.product_id.barcode = record.barcode
else:
raise UserError('Barcode sudah terisi')
-
+
+
class CheckKoli(models.Model):
_name = 'check.koli'
_description = 'Check Koli'
@@ -1885,7 +1934,8 @@ class CheckKoli(models.Model):
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'
@@ -1929,18 +1979,18 @@ class ScanKoli(models.Model):
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
+ 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!'))
@@ -1951,7 +2001,7 @@ class ScanKoli(models.Model):
existing_koli = self.search([
('picking_id', '=', record.picking_id.id),
('koli_id', '=', record.koli_id.id),
- ('id', '!=', record.id)
+ ('id', '!=', record.id)
])
if existing_koli:
raise ValidationError(f"⚠️ Koli '{record.koli_id.display_name}' sudah discan untuk picking ini!")
@@ -1974,22 +2024,23 @@ class ScanKoli(models.Model):
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."))
-
+ 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')
+ @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:
+ 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
@@ -1998,7 +2049,7 @@ class ScanKoli(models.Model):
if not scan.picking_id:
scan.scan_koli_progress = "0/0"
continue
-
+
try:
all_scans = self.env['scan.koli'].search([('picking_id', '=', scan.picking_id.id)], order='id')
if all_scans:
@@ -2010,16 +2061,17 @@ class ScanKoli(models.Model):
except Exception:
# Fallback in case of any error
scan.scan_koli_progress = "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)])
@@ -2033,22 +2085,22 @@ class ScanKoli(models.Model):
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)])
@@ -2056,21 +2108,23 @@ class ScanKoli(models.Model):
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
+ 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)])
+ 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.")
- 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
- # return remaining_scans
class KonfirmKoli(models.Model):
_name = 'konfirm.koli'
@@ -2099,7 +2153,8 @@ class KonfirmKoli(models.Model):
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'
@@ -2112,5 +2167,3 @@ class WarningModalWizard(models.TransientModel):
if self.picking_id:
return self.picking_id.with_context(skip_koli_check=True).button_validate()
return {'type': 'ir.actions.act_window_close'}
-
-