summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiqdad <ahmadmiqdad27@gmail.com>2025-07-14 10:46:41 +0700
committerMiqdad <ahmadmiqdad27@gmail.com>2025-07-14 10:46:41 +0700
commit5af60fda5cd584011e3f29b57ef018d7d66aee21 (patch)
treee214b4e17b4f0463e3b258c7587e0bd3cbefb0f4
parent8ff2da221c3e744706a69b0f8016f65169b61aca (diff)
<miqdad> date done == accounting date
-rw-r--r--indoteknik_custom/models/stock_picking.py97
1 files changed, 53 insertions, 44 deletions
diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py
index 69718c7e..afebb5d9 100644
--- a/indoteknik_custom/models/stock_picking.py
+++ b/indoteknik_custom/models/stock_picking.py
@@ -20,8 +20,10 @@ _logger = logging.getLogger(__name__)
_biteship_url = "https://api.biteship.com/v1"
biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo"
+
+
# biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA"
-
+
class StockPicking(models.Model):
_inherit = 'stock.picking'
@@ -278,16 +280,21 @@ class StockPicking(models.Model):
biteship_driver_plate_number = fields.Char('Biteship Driver Plate Number')
biteship_courier_link = fields.Char('Biteship Courier Link')
biteship_shipping_status = fields.Char('Biteship Shipping Status', help="Status pengiriman dari Biteship")
- biteship_shipping_price = fields.Monetary('Biteship Shipping Price', currency_field='currency_id', help="Harga pengiriman dari Biteship")
+ biteship_shipping_price = fields.Monetary('Biteship Shipping Price', currency_field='currency_id',
+ help="Harga pengiriman dari Biteship")
currency_id = fields.Many2one('res.currency', related='sale_id.currency_id', string='Currency', readonly=True)
final_seq = fields.Float(string='Remaining Time')
- shipping_method_so_id = fields.Many2one('delivery.carrier', string='Shipping Method', related='sale_id.carrier_id', help="Shipping Method yang digunakan di SO")
- shipping_option_so_id = fields.Many2one('shipping.option', string='Shipping Option', related='sale_id.shipping_option_id' , help="Shipping Option yang digunakan di SO")
+ shipping_method_so_id = fields.Many2one('delivery.carrier', string='Shipping Method', related='sale_id.carrier_id',
+ help="Shipping Method yang digunakan di SO")
+ shipping_option_so_id = fields.Many2one('shipping.option', string='Shipping Option',
+ related='sale_id.shipping_option_id',
+ help="Shipping Option yang digunakan di SO")
select_shipping_option_so = fields.Selection([
('biteship', 'Biteship'),
('custom', 'Custom'),
], string='Shipping Type', related='sale_id.select_shipping_option', help="Shipping Type yang digunakan di SO")
- state_packing = fields.Selection([('not_packing', 'Belum Packing'), ('packing_done', 'Sudah Packing')], string='Packing Status')
+ 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', copy=False)
update_date_doc_kirim_add = fields.Boolean(string='Update Tanggal Kirim Lewat ADD')
@@ -298,7 +305,7 @@ class StockPicking(models.Model):
if not self.name or not self.origin:
return False
return f"{self.name}"
-
+
def _download_pod_photo(self, url):
"""Mengunduh foto POD dari URL"""
try:
@@ -307,7 +314,7 @@ class StockPicking(models.Model):
return base64.b64encode(response.content)
except Exception as e:
raise UserError(f"Gagal mengunduh foto POD: {str(e)}")
-
+
def _parse_datetime(self, dt_str):
"""Parse datetime string dari format KGX"""
try:
@@ -318,37 +325,37 @@ class StockPicking(models.Model):
return datetime.strptime(dt_str, '%Y-%m-%dT%H:%M:%S')
except ValueError:
return False
-
+
def action_get_kgx_pod(self, shipment=False):
self.ensure_one()
-
+
awb_number = shipment or self._get_kgx_awb_number()
if not awb_number:
raise UserError("Nomor AWB tidak dapat dibuat, pastikan picking memiliki name dan origin")
-
+
url = "https://kgx.co.id/get_detail_awb"
headers = {'Content-Type': 'application/json'}
- payload = {"params" : {'awb_number': awb_number}}
-
+ payload = {"params": {'awb_number': awb_number}}
+
try:
response = requests.post(url, headers=headers, data=json.dumps(payload))
response.raise_for_status()
data = response.json()
-
+
if data.get('result', {}).get('data', []):
pod_data = data['result']['data'][0].get('connote_pod', {})
photo_url = pod_data.get('photo')
-
+
self.kgx_pod_photo_url = photo_url
self.kgx_pod_signature = pod_data.get('signature')
self.kgx_pod_receiver = pod_data.get('receiver')
self.kgx_pod_receive_time = self._parse_datetime(pod_data.get('timeReceive'))
self.driver_arrival_date = self._parse_datetime(pod_data.get('timeReceive'))
-
+
return data
else:
raise UserError(f"Tidak ditemukan data untuk AWB: {awb_number}")
-
+
except requests.exceptions.RequestException as e:
raise UserError(f"Gagal mengambil data POD: {str(e)}")
@@ -695,8 +702,9 @@ class StockPicking(models.Model):
raise UserError(f"Order ini sudah dikirim ke Biteship. Dengan Tracking Id: {self.biteship_tracking_id}")
if self.sale_id.select_shipping_option == 'custom':
- raise UserError("Shipping Option pada Sales Order ini adalah *Custom*. Tidak dapat dikirim melalui Biteship.")
-
+ raise UserError(
+ "Shipping Option pada Sales Order ini adalah *Custom*. Tidak dapat dikirim melalui Biteship.")
+
def is_courier_need_coordinates(service_code):
return service_code in [
"instant", "same_day", "instant_car",
@@ -793,11 +801,11 @@ class StockPicking(models.Model):
self.message_post(
body=f"Biteship berhasil dilakukan.<br/>"
- f"Kurir: {self.carrier_id.name}<br/>"
- f"Tracking ID: {self.biteship_tracking_id or '-'}<br/>"
- f"Resi: {waybill_id or '-'}<br/>"
- f"Reference: {self.name}<br/>"
- f"SO: {self.sale_id.name}",
+ f"Kurir: {self.carrier_id.name}<br/>"
+ f"Tracking ID: {self.biteship_tracking_id or '-'}<br/>"
+ f"Resi: {waybill_id or '-'}<br/>"
+ f"Reference: {self.name}<br/>"
+ f"SO: {self.sale_id.name}",
message_type="comment"
)
@@ -939,6 +947,9 @@ class StockPicking(models.Model):
pending_section = None
# Invoice values.
invoice_vals = order._prepare_invoice()
+ invoice_date = self.date_done
+ invoice_vals['date'] = invoice_date
+ invoice_vals['invoice_date'] = invoice_date
# Invoice line values (keep only necessary sections).
for line in self.move_ids_without_package:
po_line = self.env['purchase.order.line'].search(
@@ -1304,7 +1315,6 @@ class StockPicking(models.Model):
)
)
-
self.validation_minus_onhand_quantity()
self.responsible = self.env.user.id
# self.send_koli_to_so()
@@ -1355,7 +1365,7 @@ class StockPicking(models.Model):
if 'BU/PUT' in self.name:
self.automatic_reserve_product()
return res
-
+
def automatic_reserve_product(self):
if self.state == 'done':
po = self.env['purchase.order'].search([
@@ -1373,11 +1383,12 @@ class StockPicking(models.Model):
continue
invoice = self.env['account.move'].search(
- [('sale_id', '=', picking.sale_id.id), ('state', 'not in', ['draft', 'cancel']), ('move_type', '=', 'out_invoice')], limit=1)
+ [('sale_id', '=', picking.sale_id.id), ('state', 'not in', ['draft', 'cancel']),
+ ('move_type', '=', 'out_invoice')], limit=1)
if not invoice:
continue
-
+
if not picking.so_lama and invoice and (not picking.date_doc_kirim or not invoice.invoice_date):
raise UserError("Tanggal Kirim atau Tanggal Invoice belum diisi!")
@@ -1661,7 +1672,7 @@ class StockPicking(models.Model):
self.ensure_one()
order = self.env['sale.order'].search([('name', '=', self.sale_id.name)], limit=1)
-
+
sale_order_delay = self.env['sale.order.delay'].search([('so_number', '=', order.name)], limit=1)
product_shipped = []
@@ -1676,11 +1687,12 @@ class StockPicking(models.Model):
'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': ''
},
- 'delivered_date': self.driver_departure_date.strftime('%d %b %Y') if self.driver_departure_date != False else '-',
+ 'delivered_date': self.driver_departure_date.strftime(
+ '%d %b %Y') if self.driver_departure_date != False else '-',
'delivered': False,
'status': self.shipping_status,
'waybill_number': self.delivery_tracking_no or '-',
@@ -1703,7 +1715,7 @@ class StockPicking(models.Model):
elif sale_order_delay.status == 'early':
day_start = day_start - sale_order_delay.days_delayed
day_end = day_end - sale_order_delay.days_delayed
-
+
eta_start = order.date_order + timedelta(days=day_start)
eta_end = order.date_order + timedelta(days=day_end)
formatted_eta = f"{eta_start.strftime('%d %b')} - {eta_end.strftime('%d %b %Y')}"
@@ -1732,7 +1744,7 @@ class StockPicking(models.Model):
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
-
+
manifests = []
try:
@@ -1769,9 +1781,9 @@ class StockPicking(models.Model):
"manifests": [],
"delivered": False
}
- 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)}
# ACTION GET TRACKING MANUAL BITESHIP
# def action_sync_biteship_tracking(self):
@@ -1855,7 +1867,6 @@ class StockPicking(models.Model):
return description_map.get(status, f"Status '{status}' diterima dari Biteship")
-
def log_biteship_event_from_webhook(self, status, timestamp, description, extra_data=None):
self.ensure_one()
updated_fields = {}
@@ -1916,7 +1927,7 @@ class StockPicking(models.Model):
# Hindari log duplikat
if not self._has_existing_log(log_line):
- biteship_user = self.env['res.users'].sudo().browse(15710) # ID live
+ biteship_user = self.env['res.users'].sudo().browse(15710) # ID live
# biteship_user = self.env['res.users'].sudo().browse(15710) # ID user (cek di db)
self.sudo().message_post(
body=log_line,
@@ -1928,7 +1939,6 @@ class StockPicking(models.Model):
self.write(updated_fields)
_logger.info(f"[Webhook] Updated fields on picking {self.name}: {updated_fields}")
-
def _has_existing_log(self, log_line):
self.ensure_one()
self.env.cr.execute("""
@@ -1995,8 +2005,7 @@ class StockPicking(models.Model):
days_end = self.sale_id.estimated_arrival_days or (self.sale_id.estimated_arrival_days + 3)
start_date = self.sale_id.create_date + datetime.timedelta(days=days_start)
end_date = self.sale_id.create_date + datetime.timedelta(days=days_end)
-
-
+
add_day_start = 0
add_day_end = 0
sale_order_delay = self.env['sale.order.delay'].search([('so_number', '=', self.sale_id.name)], limit=1)
@@ -2005,11 +2014,11 @@ class StockPicking(models.Model):
add_day_start = sale_order_delay.days_delayed
add_day_end = sale_order_delay.days_delayed
elif sale_order_delay.status == 'early':
- add_day_start = -abs(sale_order_delay.days_delayed)
- add_day_end = -abs(sale_order_delay.days_delayed)
-
- fastest_eta = start_date +datetime.timedelta(days=add_day_start + add_day_start)
-
+ add_day_start = -abs(sale_order_delay.days_delayed)
+ add_day_end = -abs(sale_order_delay.days_delayed)
+
+ fastest_eta = start_date + datetime.timedelta(days=add_day_start + add_day_start)
+
longest_eta = end_date + datetime.timedelta(days=add_day_end)
format_time = '%d %b %Y'