summaryrefslogtreecommitdiff
path: root/indoteknik_custom/models/sale_order.py
diff options
context:
space:
mode:
Diffstat (limited to 'indoteknik_custom/models/sale_order.py')
-rwxr-xr-xindoteknik_custom/models/sale_order.py212
1 files changed, 183 insertions, 29 deletions
diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py
index 8a983479..14a8e688 100755
--- a/indoteknik_custom/models/sale_order.py
+++ b/indoteknik_custom/models/sale_order.py
@@ -51,6 +51,16 @@ class CancelReasonOrder(models.TransientModel):
order.confirm_cancel_order()
return {'type': 'ir.actions.act_window_close'}
+
+class ShippingOption(models.Model):
+ _name = "shipping.option"
+ _description = "Shipping Option"
+
+ name = fields.Char(string="Option Name", required=True)
+ price = fields.Float(string="Price", required=True)
+ provider = fields.Char(string="Provider")
+ etd = fields.Char(string="Estimated Delivery Time")
+ sale_order_id = fields.Many2one('sale.order', string="Sale Order", ondelete="cascade")
class SaleOrder(models.Model):
_inherit = "sale.order"
@@ -137,11 +147,13 @@ class SaleOrder(models.Model):
applied_voucher_shipping_id = fields.Many2one(comodel_name='voucher', string='Applied Voucher', copy=False)
amount_voucher_shipping_disc = fields.Float(string='Voucher Discount')
source_id = fields.Many2one('utm.source', 'Source', domain="[('id', 'in', [32, 59, 60, 61])]", required=True)
- estimated_arrival_days = fields.Integer('Estimated Arrival Days', default=0)
+ estimated_arrival_days = fields.Integer('Estimated Arrival To', default=0)
+ estimated_arrival_days_start = fields.Integer('Estimated Arrival From', default=0)
email = fields.Char(string='Email')
picking_iu_id = fields.Many2one('stock.picking', 'Picking IU')
helper_by_id = fields.Many2one('res.users', 'Helper By')
- eta_date = fields.Datetime(string='ETA Date', copy=False, compute='_compute_eta_date')
+ eta_date_start = fields.Datetime(string='ETA Date start', copy=False, compute='_compute_eta_date')
+ eta_date = fields.Datetime(string='ETA Date end', copy=False, compute='_compute_eta_date')
flash_sale = fields.Boolean(string='Flash Sale', help='Data dari web')
is_continue_transaction = fields.Boolean(string='Button Transaction', help='Data dari web')
web_approval = fields.Selection([
@@ -188,7 +200,17 @@ class SaleOrder(models.Model):
('PNR', 'Pareto Non Repeating'),
('NP', 'Non Pareto')
])
+ estimated_ready_ship_date = fields.Datetime(
+ string='ET Ready to Ship compute',
+ compute='_compute_etrts_date'
+ )
+ expected_ready_to_ship = fields.Datetime(
+ string='ET Ready to Ship',
+ copy=False,
+ store=True
+ )
shipping_method_picking = fields.Char(string='Shipping Method Picking', compute='_compute_shipping_method_picking')
+
reason_cancel = fields.Selection([
('harga_terlalu_mahal', 'Harga barang terlalu mahal'),
('harga_web_tidak_valid', 'Harga web tidak valid'),
@@ -202,12 +224,19 @@ class SaleOrder(models.Model):
('dokumen_tidak_support', 'Indoteknik tidak bisa support document yang dibutuhkan (Ex: TKDN, COO, SNI)'),
('ganti_quotation', 'Ganti Quotation'),
('testing_internal', 'Testing Internal'),
+ ('revisi_data', 'Revisi Data'),
], string='Reason for Cancel', copy=False, index=True, tracking=3)
attachment_bukti = fields.Many2one(
'ir.attachment',
string="Attachment Bukti Cancel", readonly=False,
)
nomor_so_pengganti = fields.Char(string='Nomor SO Pengganti', copy=False, tracking=3)
+ shipping_option_id = fields.Many2one("shipping.option", string="Selected Shipping Option", domain="['|', ('sale_order_id', '=', False), ('sale_order_id', '=', id)]")
+
+ @api.constrains('shipping_option_id')
+ def _check_shipping_option(self):
+ for rec in self:
+ rec.delivery_amt = rec.shipping_option_id.price
def _compute_shipping_method_picking(self):
for order in self:
@@ -255,15 +284,26 @@ class SaleOrder(models.Model):
if total_weight == 0:
raise UserError("Tidak dapat mengestimasi ongkir tanpa berat yang valid.")
-
+
if total_weight < 10:
total_weight = 10
+
self.delivery_amt = total_weight * 3000
+
+ shipping_option = self.env["shipping.option"].create({
+ "name": "Indoteknik Delivery",
+ "price": self.delivery_amt,
+ "provider": "Indoteknik",
+ "etd": "1-2 Hari",
+ "sale_order_id": self.id,
+ })
+ self.shipping_option_id = shipping_option.id
def action_estimate_shipping(self):
if self.carrier_id.id in [1, 151]:
self.action_indoteknik_estimate_shipping()
return
+
total_weight = 0
missing_weight_products = []
@@ -281,33 +321,35 @@ class SaleOrder(models.Model):
if total_weight == 0:
raise UserError("Tidak dapat mengestimasi ongkir tanpa berat yang valid.")
- # Mendapatkan city_id berdasarkan nama kota
- origin_city_name = self.warehouse_id.partner_id.kota_id.name
destination_subsdistrict_id = self.real_shipping_id.kecamatan_id.rajaongkir_id
-
if not destination_subsdistrict_id:
- raise UserError("Gagal mendapatkan ID kota asal atau tujuan.")
+ raise UserError("Gagal mendapatkan ID kota tujuan.")
result = self._call_rajaongkir_api(total_weight, destination_subsdistrict_id)
if result:
- estimated_cost = result['rajaongkir']['results'][0]['costs'][0]['cost'][0]['value']
- self.delivery_amt = estimated_cost
-
- shipping_info = []
+ shipping_options = []
for courier in result['rajaongkir']['results']:
for cost_detail in courier['costs']:
service = cost_detail['service']
description = cost_detail['description']
etd = cost_detail['cost'][0]['etd']
value = cost_detail['cost'][0]['value']
- shipping_info.append(f"Service: {service}, Description: {description}, ETD: {etd} hari, Cost: Rp {value}")
+ shipping_options.append((service, description, etd, value, courier['code']))
+
+ self.env["shipping.option"].search([('sale_order_id', '=', self.id)]).unlink()
+
+ for service, description, etd, value, provider in shipping_options:
+ self.env["shipping.option"].create({
+ "name": service,
+ "price": value,
+ "provider": provider,
+ "etd": etd,
+ "sale_order_id": self.id,
+ })
- log_message = "<br/>".join(shipping_info)
+ self.shipping_option_id = self.env["shipping.option"].search([('sale_order_id', '=', self.id)], limit=1).id
- description_ongkir = result['rajaongkir']['results'][0]['costs'][0]['description']
- etd_ongkir = result['rajaongkir']['results'][0]['costs'][0]['cost'][0]['etd']
- service_ongkir = result['rajaongkir']['results'][0]['costs'][0]['service']
- self.message_post(body=f"Estimasi Ongkos Kirim: Rp{self.delivery_amt}<br/>Service: {service_ongkir}<br/>Description: {description_ongkir}<br/>ETD: {etd_ongkir}<br/>Detail Lain:<br/>{log_message}")
+ self.message_post(body=f"Estimasi Ongkos Kirim: Rp{self.delivery_amt}<br/>Detail Lain:<br/>{'<br/>'.join([f'Service: {s[0]}, Description: {s[1]}, ETD: {s[2]} hari, Cost: Rp {s[3]}' for s in shipping_options])}")
else:
raise UserError("Gagal mendapatkan estimasi ongkir.")
@@ -435,19 +477,126 @@ class SaleOrder(models.Model):
rec.compute_fullfillment = True
+ @api.depends('date_order', 'estimated_arrival_days', 'state', 'estimated_arrival_days_start')
def _compute_eta_date(self):
- max_leadtime = 0
+ for rec in self:
+ if rec.date_order and rec.state not in ['cancel'] and rec.estimated_arrival_days and rec.estimated_arrival_days_start:
+ rec.eta_date = rec.date_order + timedelta(days=rec.estimated_arrival_days)
+ rec.eta_date_start = rec.date_order + timedelta(days=rec.estimated_arrival_days_start)
+ else:
+ rec.eta_date = False
+ rec.eta_date_start = False
+
+
+ def get_days_until_next_business_day(self,start_date=None, *args, **kwargs):
+ today = start_date or datetime.today().date()
+ offset = 0 # Counter jumlah hari yang ditambahkan
+ holiday = self.env['hr.public.holiday']
+
+ while True :
+ today += timedelta(days=1)
+ offset += 1
+
+ if today.weekday() >= 5:
+ continue
- for line in self.order_line:
- leadtime = line.vendor_id.leadtime
- max_leadtime = max(max_leadtime, leadtime)
+ is_holiday = holiday.search([("start_date", "=", today)])
+ if is_holiday:
+ continue
+
+ break
+
+ return offset
+
+ # def calculate_sla_by_vendor(self, products):
+ # slatime = 15
+ # for line in products:
+ # product_sla = self.env['product.sla'].search([('product_variant_id', '=', line.product_id.id)], limit=1)
+ # slatime = int(product_sla.sla) if product_sla and product_sla.sla and product_sla.sla != 'Indent' and "hari" in product_sla.sla.lower() else 15
+
+ # return {
+ # 'slatime' : slatime
+ # }
+
+ def calculate_sla_by_vendor(self, products):
+ product_ids = products.mapped('product_id.id') # Kumpulkan semua ID produk
+ include_instant = True # Default True, tetapi bisa menjadi False
+
+ # Cek apakah SEMUA produk memiliki qty_free_bandengan >= qty_needed
+ all_fast_products = all(product.product_id.qty_free_bandengan >= product.product_uom_qty for product in products)
+ if all_fast_products:
+ return {'slatime': 1, 'include_instant': include_instant}
+
+
+ # Cari semua vendor pemenang untuk produk yang diberikan
+ vendors = self.env['purchase.pricelist'].search([
+ ('product_id', 'in', product_ids),
+ ('is_winner', '=', True)
+ ])
+
+ max_slatime = 1
+
+ for vendor in vendors:
+ vendor_sla = self.env['vendor.sla'].search([('id_vendor', '=', vendor.vendor_id.id)], limit=1)
+ slatime = 15
+ if vendor_sla:
+ if vendor_sla.unit == 'hari':
+ vendor_duration = vendor_sla.duration * 24 * 60
+ include_instant = False
+ else :
+ vendor_duration = vendor_sla.duration * 60
+ include_instant = True
+
+ estimation_sla = (1 * 24 * 60) + vendor_duration
+ estimation_sla_days = estimation_sla / (24 * 60)
+ slatime = math.ceil(estimation_sla_days)
+
+ max_slatime = max(max_slatime, slatime)
+ return {'slatime': max_slatime, 'include_instant': include_instant}
+
+ @api.depends("order_line.product_id")
+ def _compute_etrts_date(self): #Function to calculate Estimated Ready To Ship Date
for rec in self:
- if rec.date_order and rec.state not in ['cancel', 'draft']:
- eta_date = datetime.now() + timedelta(days=max_leadtime)
- rec.eta_date = eta_date
- else:
- rec.eta_date = False
+ max_slatime = 1 # Default SLA jika tidak ada
+ slatime = self.calculate_sla_by_vendor(rec.order_line)
+ max_slatime = max(max_slatime, slatime['slatime'])
+
+ if rec.date_order:
+ sum_days = max_slatime + self.get_days_until_next_business_day(rec.date_order) - 1
+ if not rec.estimated_arrival_days:
+ rec.estimated_arrival_days = sum_days
+
+ eta_date = rec.date_order + timedelta(days=sum_days)
+ rec.estimated_ready_ship_date = eta_date
+ rec.commitment_date = eta_date
+ # Jika expected_ready_to_ship kosong, set nilai default
+ if not rec.expected_ready_to_ship:
+ rec.expected_ready_to_ship = eta_date
+
+
+
+ @api.onchange('expected_ready_to_ship') #Hangle Onchange form Expected Ready to Ship
+ def _onchange_expected_ready_ship_date(self):
+ for rec in self:
+ if rec.expected_ready_to_ship and rec.estimated_ready_ship_date:
+ # Hanya membandingkan tanggal saja, tanpa jam
+ expected_date = rec.expected_ready_to_ship.date()
+ estimated_date = rec.estimated_ready_ship_date.date()
+
+ if expected_date < estimated_date:
+ rec.expected_ready_to_ship = rec.estimated_ready_ship_date
+ rec.commitment_date = rec.estimated_ready_ship_date
+ raise ValidationError(
+ "Tanggal 'Expected Ready to Ship' tidak boleh lebih kecil dari {}. Mohon pilih tanggal minimal {}."
+ .format(estimated_date.strftime('%d-%m-%Y'), estimated_date.strftime('%d-%m-%Y'))
+ )
+
+ def _set_etrts_date(self):
+ for order in self:
+ if order.state in ('done', 'cancel', 'sale'):
+ raise UserError(_("You cannot change the Estimated Ready To Ship Date on a done, sale or cancelled order."))
+ # order.move_lines.write({'estimated_ready_ship_date': order.estimated_ready_ship_date})
def _prepare_invoice(self):
"""
@@ -642,15 +791,16 @@ class SaleOrder(models.Model):
if line.product_id.type == 'product':
line_no += 1
line.line_no = line_no
+
def write(self, vals):
res = super(SaleOrder, self).write(vals)
-
+ # self._compute_etrts_date()
if 'carrier_id' in vals:
for picking in self.picking_ids:
if picking.state == 'assigned':
picking.carrier_id = self.carrier_id
-
+
return res
def calculate_so_status(self):
@@ -1108,6 +1258,7 @@ class SaleOrder(models.Model):
order._set_sppkp_npwp_contact()
order.calculate_line_no()
order.send_notif_to_salesperson()
+ order._compute_etrts_date()
# order.order_line.get_reserved_from()
res = super(SaleOrder, self).action_confirm()
@@ -1503,13 +1654,14 @@ class SaleOrder(models.Model):
def create(self, vals):
# Ensure partner details are updated when a sale order is created
order = super(SaleOrder, self).create(vals)
+ order._compute_etrts_date()
# order._update_partner_details()
return order
def write(self, vals):
# Call the super method to handle the write operation
res = super(SaleOrder, self).write(vals)
-
+ # self._compute_etrts_date()
# Check if the update is coming from a save operation
# if any(field in vals for field in ['sppkp', 'npwp', 'email', 'customer_type']):
# self._update_partner_details()
@@ -1544,4 +1696,6 @@ class SaleOrder(models.Model):
raise UserError(
"SO tidak dapat ditambahkan produk baru karena SO sudah menjadi sale order.")
res = super(SaleOrder, self).write(vals)
+ if 'order_line' in vals:
+ self._compute_etrts_date()
return res \ No newline at end of file