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.py242
1 files changed, 151 insertions, 91 deletions
diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py
index 77fee068..e7dd582d 100755
--- a/indoteknik_custom/models/sale_order.py
+++ b/indoteknik_custom/models/sale_order.py
@@ -234,9 +234,9 @@ class SaleOrder(models.Model):
customer_type = fields.Selection([
('pkp', 'PKP'),
('nonpkp', 'Non PKP')
- ], required=True, compute='_compute_partner_field')
- sppkp = fields.Char(string="SPPKP", required=True, tracking=True, compute='_compute_partner_field')
- npwp = fields.Char(string="NPWP", required=True, tracking=True, compute='_compute_partner_field')
+ ], related="partner_id.customer_type", string="Customer Type", readonly=True)
+ sppkp = fields.Char(string="SPPKP", related="partner_id.sppkp")
+ npwp = fields.Char(string="NPWP", related="partner_id.npwp")
purchase_total = fields.Monetary(string='Purchase Total', compute='_compute_purchase_total')
voucher_id = fields.Many2one(comodel_name='voucher', string='Voucher', copy=False)
applied_voucher_id = fields.Many2one(comodel_name='voucher', string='Applied Voucher', copy=False)
@@ -358,7 +358,6 @@ class SaleOrder(models.Model):
help="Tanggal pertama kali barang berhasil di-reservasi pada DO (BU/PICK/) yang berstatus Siap Dikirim."
)
refund_ids = fields.Many2many('refund.sale.order', compute='_compute_refund_ids', string='Refunds')
- has_refund = fields.Boolean(string='Has Refund', compute='_compute_has_refund')
refund_count = fields.Integer(string='Refund Count', compute='_compute_refund_count')
advance_payment_move_id = fields.Many2one(
'account.move',
@@ -394,6 +393,26 @@ class SaleOrder(models.Model):
('paid', 'Full Paid'),
('no_invoice', 'No Invoice'),
], string="Payment Status Invoice", compute="_compute_payment_state_custom", store=False)
+ partner_is_cbd_locked = fields.Boolean(
+ string="Partner Locked CBD",
+ compute="_compute_partner_is_cbd_locked"
+ )
+
+ @api.depends('partner_id.is_cbd_locked')
+ def _compute_partner_is_cbd_locked(self):
+ for order in self:
+ order.partner_is_cbd_locked = order.partner_id.is_cbd_locked
+
+
+ @api.constrains('payment_term_id', 'partner_id', 'state')
+ def _check_cbd_lock_sale_order(self):
+ # cbd_term = self.env['account.payment.term'].browse(26)
+ for rec in self:
+ if rec.state == 'draft' and rec.partner_id.is_cbd_locked:
+ # if rec.payment_term_id and rec.payment_term_id != cbd_term:
+ raise ValidationError(
+ "Customer ini terkunci ke CBD, hanya boleh pakai Payment Term CBD."
+ )
@api.depends('invoice_ids.payment_state', 'invoice_ids.amount_total', 'invoice_ids.amount_residual')
def _compute_payment_state_custom(self):
@@ -2030,22 +2049,22 @@ class SaleOrder(models.Model):
# return [('id', 'not in', order_ids)]
# return ['&', ('order_line.invoice_lines.move_id.move_type', 'in', ('out_invoice', 'out_refund')), ('order_line.invoice_lines.move_id', operator, value)]
- @api.depends('partner_id')
- def _compute_partner_field(self):
- for order in self:
- partner = order.partner_id.parent_id or order.partner_id
- order.npwp = partner.npwp
- order.sppkp = partner.sppkp
- order.customer_type = partner.customer_type
+ # @api.depends('partner_id')
+ # def _compute_partner_field(self):
+ # for order in self:
+ # partner = order.partner_id.parent_id or order.partner_id
+ # order.npwp = partner.npwp
+ # order.sppkp = partner.sppkp
+ # order.customer_type = partner.customer_type
@api.onchange('partner_id')
def onchange_partner_contact(self):
parent_id = self.partner_id.parent_id
parent_id = parent_id if parent_id else self.partner_id
- self.npwp = parent_id.npwp
- self.sppkp = parent_id.sppkp
- self.customer_type = parent_id.customer_type
+ # self.npwp = parent_id.npwp
+ # self.sppkp = parent_id.sppkp
+ # self.customer_type = parent_id.customer_type
self.email = parent_id.email
self.pareto_status = parent_id.pareto_status
self.user_id = parent_id.user_id
@@ -2116,15 +2135,21 @@ class SaleOrder(models.Model):
if self.payment_term_id.id == 31 and self.total_percent_margin < 25:
raise UserError("Jika ingin menggunakan Tempo 90 Hari maka margin harus di atas 25%")
- if self.warehouse_id.id != 8 and self.warehouse_id.id != 10: # GD Bandengan
- raise UserError('Gudang harus Bandengan')
+ if self.warehouse_id.id != 8 and self.warehouse_id.id != 10 and self.warehouse_id.id != 12: # GD Bandengan / Pameran
+ raise UserError('Gudang harus Bandengan atau Pameran')
if self.state not in ['draft', 'sent']:
raise UserError("Status harus draft atau sent")
- self._validate_npwp()
-
def _validate_npwp(self):
+ if not self.npwp:
+ raise UserError("NPWP partner kosong, silahkan isi terlebih dahulu npwp nya di contact partner")
+
+ if not self.customer_type:
+ raise UserError("Customer Type partner kosong, silahkan isi terlebih dahulu Customer Type nya di contact partner")
+
+ if not self.sppkp:
+ raise UserError("SPPKP partner kosong, silahkan isi terlebih dahulu SPPKP nya di contact partner")
num_digits = sum(c.isdigit() for c in self.npwp)
if num_digits < 10:
@@ -2138,6 +2163,7 @@ class SaleOrder(models.Model):
self._validate_order()
for order in self:
+ order._validate_npwp()
order._validate_uniform_taxes()
order.order_line.validate_line()
@@ -2192,9 +2218,8 @@ class SaleOrder(models.Model):
if self.validate_different_vendor() and not self.vendor_approval:
return self._create_notification_action('Notification', 'Terdapat Vendor yang berbeda dengan MD Vendor')
self.check_due()
-
- self._validate_order()
for order in self:
+ order._validate_npwp()
order._validate_delivery_amt()
order._validate_uniform_taxes()
order.order_line.validate_line()
@@ -2467,6 +2492,7 @@ class SaleOrder(models.Model):
order.check_data_real_delivery_address()
order.sale_order_check_approve()
order._validate_order()
+ order._validate_npwp()
order.order_line.validate_line()
main_parent = order.partner_id.get_main_parent()
@@ -2652,23 +2678,17 @@ class SaleOrder(models.Model):
def _set_sppkp_npwp_contact(self):
partner = self.partner_id.parent_id or self.partner_id
- if not partner.sppkp:
- partner.sppkp = self.sppkp
- if not partner.npwp:
- 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.customer_type:
+ # partner.customer_type = self.customer_type
if not partner.user_id:
partner.user_id = self.user_id.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
- # partner.sppkp = self.sppkp
- # partner.email = self.email
-
def _compute_total_margin(self):
for order in self:
total_margin = sum(line.item_margin for line in order.order_line if line.product_id)
@@ -3110,52 +3130,6 @@ class SaleOrder(models.Model):
# 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()
-
- # return res
-
- def _update_partner_details(self):
- for order in self:
- partner = order.partner_id.parent_id or order.partner_id
- if partner:
- # Update partner details
- partner.sppkp = order.sppkp
- partner.npwp = order.npwp
- partner.email = order.email
- partner.customer_type = order.customer_type
-
- # Save changes to the partner record
- partner.write({
- 'sppkp': partner.sppkp,
- 'npwp': partner.npwp,
- 'email': partner.email,
- 'customer_type': partner.customer_type,
- })
-
- # def write(self, vals):
- # for order in self:
- # if order.state in ['sale', 'cancel']:
- # if 'order_line' in vals:
- # new_lines = vals.get('order_line', [])
- # for command in new_lines:
- # if command[0] == 0: # A new line is being added
- # raise UserError(
- # "SO tidak dapat ditambahkan produk baru karena SO sudah menjadi sale order.")
- #
- # res = super(SaleOrder, self).write(vals)
- # # self._check_total_margin_excl_third_party()
- # if any(fields in vals for fields in ['delivery_amt', 'carrier_id', 'shipping_cost_covered']):
- # self._validate_delivery_amt()
- # if any(field in vals for field in ["order_line", "client_order_ref"]):
- # self._calculate_etrts_date()
- # return res
-
# @api.depends('commitment_date')
def _compute_ready_to_ship_status_detail(self):
def is_empty(val):
@@ -3284,20 +3258,58 @@ class SaleOrder(models.Model):
def button_refund(self):
self.ensure_one()
+ if self.state not in ['cancel', 'sale']:
+ raise UserError(f"❌ SO {self.name} tidak bisa direfund. Status harus Cancel atau Sale.")
+ if self.state == 'sale':
+ not_done_pickings = self.picking_ids.filtered(lambda p: p.state not in ['done', 'cancel'])
+ if not_done_pickings:
+ raise UserError(
+ f"❌ SO {self.name} Belum melakukan kirim barang "
+ f"({', '.join(not_done_pickings.mapped('name'))}). Selesaikan Pengiriman untuk melakukan refund."
+ )
+ moves = self.env['account.move'].search([
+ ('sale_id', '=', self.id),
+ ('journal_id', '=', 11),
+ ('state', '=', 'posted'),
+ ])
+
+ # Default 0
+ total_uang_muka = 0.0
+
+ has_moves = bool(moves)
+ has_settlement = self.payment_status == 'settlement'
+
+ if has_moves and has_settlement:
+ total_uang_muka = sum(moves.mapped('amount_total_signed')) + self.gross_amount
+ elif has_moves:
+ total_uang_muka = sum(moves.mapped('amount_total_signed'))
+ elif has_settlement:
+ total_uang_muka = self.gross_amount
+ else:
+ raise UserError(
+ "Tidak bisa melakukan refund karena SO tidak memiliki Record Uang Masuk "
+ "(Journal Uang Muka/Midtrans Payment)."
+ )
invoice_ids = self.invoice_ids.filtered(lambda inv: inv.state != 'cancel')
+ total_refunded = sum(self.refund_ids.mapped('amount_refund'))
+ sisa_uang_muka = total_uang_muka - total_refunded
+
+ if sisa_uang_muka <= 0:
+ raise UserError("❌ Tidak ada sisa transaksi untuk di-refund. Semua dana sudah dikembalikan.")
return {
'name': 'Refund Sale Order',
'type': 'ir.actions.act_window',
'res_model': 'refund.sale.order',
'view_mode': 'form',
+ 'target':'new',
'target': 'current',
'context': {
'default_sale_order_ids': [(6, 0, [self.id])],
'default_invoice_ids': [(6, 0, invoice_ids.ids)],
- 'default_uang_masuk': sum(invoice_ids.mapped('amount_total')) + (self.delivery_amt or 0.0) + 1000,
+ 'default_uang_masuk': sisa_uang_muka,
'default_ongkir': self.delivery_amt or 0.0,
- 'default_bank': '', # bisa isi default bank kalau mau
+ 'default_bank': '',
'default_account_name': '',
'default_account_no': '',
'default_refund_type': '',
@@ -3308,6 +3320,30 @@ class SaleOrder(models.Model):
if not self:
raise UserError("Tidak ada Sale Order yang dipilih.")
+ if len(self) > 1:
+ not_cancel_orders = self.filtered(lambda so: so.state != 'cancel')
+ if not_cancel_orders:
+ raise ValidationError(
+ f"❌ Refund Multi SO hanya bisa dibuat untuk SO dengan status Cancel. "
+ f"SO berikut tidak Cancel: {', '.join(not_cancel_orders.mapped('name'))}"
+ )
+
+
+ invalid_status_orders = []
+ for order in self:
+ if order.state not in ['cancel', 'sale']:
+ invalid_status_orders.append(order.name)
+ elif order.state == 'sale':
+ not_done_pickings = order.picking_ids.filtered(lambda p: p.state != 'done')
+ if not_done_pickings:
+ invalid_status_orders.append(order.name)
+
+ if invalid_status_orders:
+ raise ValidationError(
+ f"❌ Refund tidak bisa dibuat untuk SO {', '.join(invalid_status_orders)}. "
+ f"SO harus Cancel atau Sale dengan semua Pengiriman sudah selesai."
+ )
+
partner_set = set(self.mapped('partner_id.id'))
if len(partner_set) > 1:
raise UserError("Tidak dapat membuat refund untuk Multi SO dengan Customer berbeda. Harus memiliki Customer yang sama.")
@@ -3316,14 +3352,43 @@ class SaleOrder(models.Model):
if len(invoice_status_set) > 1:
raise UserError("Tidak dapat membuat refund untuk SO dengan status invoice berbeda. Harus memiliki status invoice yang sama.")
- already_refunded = self.filtered(lambda so: so.has_refund)
- if already_refunded:
- so_names = ', '.join(already_refunded.mapped('name'))
- raise UserError(f"❌ Tidak bisa refund ulang. {so_names} sudah melakukan refund.")
+ refunded_orders = self.filtered(lambda so: self.env['refund.sale.order'].search([('sale_order_ids', 'in', so.id)], limit=1))
+ if refunded_orders:
+ raise ValidationError(
+ f"SO {', '.join(refunded_orders.mapped('name'))} sudah pernah di-refund dan tidak bisa ikut dalam refund Multi SO."
+ )
+
+ total_uang_masuk = 0.0
+ invalid_orders=[]
+ for order in self:
+ moves = self.env['account.move'].search([
+ ('sale_id', '=', order.id),
+ ('journal_id', '=', 11),
+ ('state', '=', 'posted'),
+ ])
+
+ total_uang_muka = 0.0
+
+ if moves and order.payment_status == 'settlement':
+ total_uang_muka = order.gross_amount + sum(moves.mapped('amount_total_signed')) or 0.0
+ elif moves:
+ total_uang_muka = sum(moves.mapped('amount_total_signed')) or 0.0
+ elif order.payment_status == 'settlement':
+ total_uang_muka = order.gross_amount
+ else:
+ invalid_orders.append(order.name)
+
+ total_uang_masuk += total_uang_muka
+
+ if invalid_orders:
+ raise ValidationError(
+ f"Tidak dapat membuat refund untuk SO {', '.join(invalid_orders)} karena tidak memiliki Record Uang Masuk (Journal Uang Muka/Midtrans).\n"
+ "Pastikan semua SO yang dipilih sudah memiliki Record pembayaran yang valid."
+ )
+
invoice_ids = self.mapped('invoice_ids').filtered(lambda inv: inv.state != 'cancel')
delivery_total = sum(self.mapped('delivery_amt'))
- total_invoice = sum(invoice_ids.mapped('amount_total'))
return {
'type': 'ir.actions.act_window',
@@ -3334,7 +3399,7 @@ class SaleOrder(models.Model):
'context': {
'default_sale_order_ids': [(6, 0, self.ids)],
'default_invoice_ids': [(6, 0, invoice_ids.ids)],
- 'default_uang_masuk': total_invoice + delivery_total + 1000,
+ 'default_uang_masuk': total_uang_masuk,
'default_ongkir': delivery_total,
'default_bank': '',
'default_account_name': '',
@@ -3343,11 +3408,6 @@ class SaleOrder(models.Model):
}
}
- @api.depends('refund_ids')
- def _compute_has_refund(self):
- for so in self:
- so.has_refund = bool(so.refund_ids)
-
def action_view_related_refunds(self):
self.ensure_one()
return {