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.py208
1 files changed, 177 insertions, 31 deletions
diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py
index 8f48e898..1ad08154 100755
--- a/indoteknik_custom/models/sale_order.py
+++ b/indoteknik_custom/models/sale_order.py
@@ -78,6 +78,7 @@ class SaleOrder(models.Model):
payment_link_midtrans = fields.Char(string='Payment Link', help='Url payment yg digenerate oleh midtrans, harap diserahkan ke customer agar dapat dilakukan pembayaran secara mandiri')
payment_qr_code = fields.Binary("Payment QR Code")
due_id = fields.Many2one('due.extension', string="Due Extension", readonly=True, tracking=True)
+ vendor_approval_id = fields.Many2one('vendor.approval', string="Vendor Approval", readonly=True, tracking=True)
customer_type = fields.Selection([
('pkp', 'PKP'),
('nonpkp', 'Non PKP')
@@ -104,6 +105,7 @@ class SaleOrder(models.Model):
('cust_procurement', 'Customer Procurement')
], string='Web Approval', copy=False)
compute_fullfillment = fields.Boolean(string='Compute Fullfillment', compute="_compute_fullfillment")
+ vendor_approval = fields.Boolean(string='Vendor Approval')
note_ekspedisi = fields.Char(string="Note Ekspedisi")
date_kirim_ril = fields.Datetime(string='Tanggal Kirim SJ', compute='_compute_date_kirim', copy=False)
date_status_done = fields.Datetime(string='Date Done DO', compute='_compute_date_kirim', copy=False)
@@ -133,49 +135,112 @@ class SaleOrder(models.Model):
'account.payment.term', string='Payment Terms', check_company=True, # Unrequired company
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]", tracking=True)
+ total_weight = fields.Float(string='Total Weight', compute='_compute_total_weight')
+ pareto_status = fields.Selection([
+ ('PR', 'Pareto Repeating'),
+ ('PPR', 'Potensi Pareto Repeating'),
+ ('PNR', 'Pareto Non Repeating'),
+ ('NP', 'Non Pareto')
+ ])
+
+ def _compute_total_weight(self):
+ total_weight = 0
+ missing_weight_products = []
+
+ for line in self.order_line:
+ if line.weight:
+ total_weight += line.weight * line.product_uom_qty
+
+ self.total_weight = total_weight
+
+ def action_indoteknik_estimate_shipping(self):
+ if not self.real_shipping_id.kota_id.is_jabodetabek:
+ raise UserError('Estimasi ongkir hanya bisa dilakukan di kota Jabodetabek')
+
+ total_weight = 0
+ missing_weight_products = []
+
+ for line in self.order_line:
+ if line.weight:
+ total_weight += line.weight * line.product_uom_qty
+ line.product_id.weight = line.weight
+ else:
+ missing_weight_products.append(line.product_id.name)
+
+ if missing_weight_products:
+ product_names = '<br/>'.join(missing_weight_products)
+ self.message_post(body=f"Produk berikut tidak memiliki berat:<br/>{product_names}")
+
+ 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
+
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 = []
for line in self.order_line:
if line.weight:
- total_weight += line.weight
+ total_weight += line.weight * line.product_uom_qty
+ line.product_id.weight = line.weight
else:
missing_weight_products.append(line.product_id.name)
+ if missing_weight_products:
+ product_names = '<br/>'.join(missing_weight_products)
+ self.message_post(body=f"Produk berikut tidak memiliki berat:<br/>{product_names}")
+
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_city_name = self.real_shipping_id.kota_id.name
-
- origin_city_id = self._get_city_id_by_name(origin_city_name)
- destination_city_id = self._get_city_id_by_name(destination_city_name)
+ destination_subsdistrict_id = self.real_shipping_id.kecamatan_id.rajaongkir_id
- if not origin_city_id or not destination_city_id:
+ if not destination_subsdistrict_id:
raise UserError("Gagal mendapatkan ID kota asal atau tujuan.")
- result = self._call_rajaongkir_api(total_weight, origin_city_id, destination_city_id)
+ 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
- self.message_post(body=f"Estimasi Ongkos Kirim: {estimated_cost}")
+
+ shipping_info = []
+ 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}")
+
+ log_message = "<br/>".join(shipping_info)
+
+ 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}")
else:
raise UserError("Gagal mendapatkan estimasi ongkir.")
- def _call_rajaongkir_api(self, total_weight, origin_city_id, destination_city_id):
+ def _call_rajaongkir_api(self, total_weight, destination_subsdistrict_id):
url = 'https://pro.rajaongkir.com/api/cost'
headers = {
- 'key': '7ac9883688da043b50cc32f0e3070bb6',
+ 'key': '9b1310f644056d84d60b0af6bb21611a',
}
courier = self.carrier_id.name.lower()
data = {
- 'origin': int(origin_city_id),
- 'originType': 'city',
- 'destination': int(destination_city_id),
- 'destinationType': 'city',
+ 'origin': 2127,
+ 'originType': 'subdistrict',
+ 'destination': int(destination_subsdistrict_id),
+ 'destinationType': 'subdistrict',
'weight': int(total_weight * 1000),
'courier': courier,
}
@@ -186,16 +251,13 @@ class SaleOrder(models.Model):
return None
def _normalize_city_name(self, city_name):
- # Ubah nama kota menjadi huruf kecil
city_name = city_name.lower()
- # Hilangkan prefiks "kabupaten" atau "kota" jika ada
if city_name.startswith('kabupaten'):
city_name = city_name.replace('kabupaten', '').strip()
elif city_name.startswith('kota'):
city_name = city_name.replace('kota', '').strip()
- # Hilangkan spasi yang berlebihan
city_name = " ".join(city_name.split())
return city_name
@@ -203,10 +265,9 @@ class SaleOrder(models.Model):
def _get_city_id_by_name(self, city_name):
url = 'https://pro.rajaongkir.com/api/city'
headers = {
- 'key': '7ac9883688da043b50cc32f0e3070bb6',
+ 'key': '9b1310f644056d84d60b0af6bb21611a',
}
- # Normalisasi nama kota sebelum melakukan pencarian
normalized_city_name = self._normalize_city_name(city_name)
response = requests.get(url, headers=headers)
@@ -220,14 +281,17 @@ class SaleOrder(models.Model):
def _get_subdistrict_id_by_name(self, city_id, subdistrict_name):
url = f'https://pro.rajaongkir.com/api/subdistrict?city={city_id}'
headers = {
- 'key': '7ac9883688da043b50cc32f0e3070bb6',
+ 'key': '9b1310f644056d84d60b0af6bb21611a',
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
subdistrict_data = response.json()
for subdistrict in subdistrict_data['rajaongkir']['results']:
- if subdistrict['subdistrict_name'].lower() == subdistrict_name.lower():
+ subsdistrict_1 = subdistrict['subdistrict_name'].lower()
+ subsdistrict_2 = subdistrict_name.lower()
+
+ if subsdistrict_1 == subsdistrict_2:
return subdistrict['subdistrict_id']
return None
@@ -412,6 +476,22 @@ class SaleOrder(models.Model):
for line in order.order_line:
total += line.vendor_subtotal
order.purchase_total = total
+
+ def check_data_real_delivery_address(self):
+ real_delivery_address = self.real_shipping_id
+
+ if not real_delivery_address.state_id:
+ raise UserError('State Real Delivery Address harus diisi')
+ if not real_delivery_address.zip:
+ raise UserError('Zip code Real Delivery Address harus diisi')
+ if not real_delivery_address.mobile:
+ raise UserError('Mobile Real Delivery Address harus diisi')
+ if not real_delivery_address.phone:
+ raise UserError('Phone Real Delivery Address harus diisi')
+ if not real_delivery_address.kecamatan_id:
+ raise UserError('Kecamatan Real Delivery Address harus diisi')
+ if not real_delivery_address.kelurahan_id:
+ raise UserError('Kelurahan Real Delivery Address harus diisi')
def generate_payment_link_midtrans_sales_order(self):
# midtrans_url = 'https://app.sandbox.midtrans.com/snap/v1/transactions' # dev - sandbox
@@ -558,8 +638,6 @@ class SaleOrder(models.Model):
raise UserError('Phone Real Delivery Address harus diisi')
if not real_delivery_address.kecamatan_id:
raise UserError('Kecamatan Real Delivery Address harus diisi')
- if not real_delivery_address.kelurahan_id:
- raise UserError('Kelurahan Real Delivery Address harus diisi')
@api.onchange('partner_id')
def onchange_partner_contact(self):
@@ -570,6 +648,7 @@ class SaleOrder(models.Model):
self.sppkp = parent_id.sppkp
self.customer_type = parent_id.customer_type
self.email = parent_id.email
+ self.pareto_status = parent_id.pareto_status
@api.onchange('partner_id')
def onchange_partner_id(self):
@@ -661,10 +740,14 @@ class SaleOrder(models.Model):
for order in self:
order.order_line.validate_line()
+ term_days = 0
+ for term_line in order.payment_term_id.line_ids:
+ term_days += term_line.days
+
partner = order.partner_id.parent_id or order.partner_id
if not partner.property_payment_term_id:
raise UserError("Payment Term pada Master Data Customer harus diisi")
- if not partner.active_limit:
+ if not partner.active_limit and term_days > 0:
raise UserError("Credit Limit pada Master Data Customer harus diisi")
if order.payment_term_id != partner.property_payment_term_id:
raise UserError("Payment Term berbeda pada Master Data Customer")
@@ -678,16 +761,23 @@ class SaleOrder(models.Model):
raise UserError("Salesperson sudah tidak aktif, mohon diisi yang benar pada data SO dan Contact")
def sale_order_approve(self):
+ if self.validate_different_vendor() and not self.vendor_approval and not self.vendor_approval_id:
+ return self._create_notification_action('Notification', 'Terdapat Vendor yang berbeda dengan MD Vendor')
self.check_due()
self._validate_order()
for order in self:
order.order_line.validate_line()
+
+ term_days = 0
+ for term_line in order.payment_term_id.line_ids:
+ term_days += term_line.days
+
partner = order.partner_id.parent_id or order.partner_id
if not partner.property_payment_term_id:
raise UserError("Payment Term pada Master Data Customer harus diisi")
- if not partner.active_limit:
+ if not partner.active_limit and term_days > 0:
raise UserError("Credit Limit pada Master Data Customer harus diisi")
if order.payment_term_id != partner.property_payment_term_id:
raise UserError("Payment Term berbeda pada Master Data Customer")
@@ -781,9 +871,56 @@ class SaleOrder(models.Model):
'body_html': email_body,
'email_to': salesperson_email,
}).send()
+
+ def validate_different_vendor(self):
+ if self.vendor_approval_id and self.vendor_approval_id.state == 'draft':
+ raise UserError('SO ini sedang dalam review Vendor Approval')
+
+ if self.vendor_approval_id and self.vendor_approval_id.state == 'cancel':
+ raise UserError('Vendor Approval SO ini Di Reject')
+
+ if self.vendor_approval_id and self.vendor_approval_id.state == 'done':
+ return False
+
+ different_vendor = self.order_line.filtered(lambda l: l.vendor_id and l.vendor_md_id and l.vendor_id.id != l.vendor_md_id.id)
+ if different_vendor:
+ vendor_approval = self.env['vendor.approval'].create({
+ 'order_id': self.id,
+ 'create_date_so': self.create_date,
+ 'partner_id': self.partner_id.id,
+ 'state': 'draft',
+ })
+
+ self.vendor_approval_id = vendor_approval.id
+
+ for line in different_vendor:
+ self.env['vendor.approval.line'].create({
+ 'vendor_approval_id': vendor_approval.id,
+ 'product_id': line.product_id.id,
+ 'product_uom_qty': line.product_uom_qty,
+ 'vendor_id': line.vendor_id.id,
+ 'vendor_md_id': line.vendor_md_id.id,
+ 'purchase_price': line.purchase_price,
+ 'purchase_price_md': line.purchase_price_md,
+ 'sales_price': line.price_unit,
+ 'margin_before': line.margin_md,
+ 'margin_after': line.item_percent_margin,
+ 'purchase_tax_id': line.purchase_tax_id.id,
+ 'sales_tax_id': line.tax_id[0].id if line.tax_id else False,
+ 'percent_margin_difference': (line.price_unit - line.purchase_price_md) / line.purchase_price_md if line.purchase_price_md else False,
+ })
+
+ return True
+ else:
+ return False
+
def action_confirm(self):
for order in self:
+ if self.validate_different_vendor() and not self.vendor_approval and not self.vendor_approval_id:
+ return self._create_notification_action('Notification', 'Terdapat Vendor yang berbeda dengan MD Vendor')
+
+ order.check_data_real_delivery_address()
order.sale_order_check_approve()
order._validate_order()
order.order_line.validate_line()
@@ -896,11 +1033,20 @@ class SaleOrder(models.Model):
def _set_sppkp_npwp_contact(self):
partner = self.partner_id.parent_id or self.partner_id
- if not partner.sppkp or not partner.npwp or not partner.email or partner.customer_type:
- partner.customer_type = self.customer_type
- partner.npwp = self.npwp
+ if not partner.sppkp:
partner.sppkp = self.sppkp
+ if not partner.npwp:
+ partner.npwp = self.npwp
+ if not partner.email:
partner.email = self.email
+ if not partner.customer_type:
+ partner.customer_type = self.customer_type
+
+ # if not partner.sppkp or not partner.npwp or not partner.email or partner.customer_type:
+ # partner.customer_type = self.customer_type
+ # partner.npwp = self.npwp
+ # partner.sppkp = self.sppkp
+ # partner.email = self.email
def _compute_total_margin(self):
for order in self:
@@ -1170,7 +1316,7 @@ 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._update_partner_details()
+ # order._update_partner_details()
return order
def write(self, vals):
@@ -1178,8 +1324,8 @@ class SaleOrder(models.Model):
res = super(SaleOrder, self).write(vals)
# 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()
+ # if any(field in vals for field in ['sppkp', 'npwp', 'email', 'customer_type']):
+ # self._update_partner_details()
return res