from odoo import models, api, fields from odoo.exceptions import AccessError, UserError, ValidationError from datetime import timedelta, date, datetime import logging _logger = logging.getLogger(__name__) class ApprovalPaymentTerm(models.Model): _name = "approval.payment.term" _description = "Approval Payment Term" _inherit = ['mail.thread'] _rec_name = 'number' number = fields.Char(string='Document No', index=True, copy=False, readonly=True, tracking=True) partner_id = fields.Many2one('res.partner', string='Partner', copy=False) property_payment_term_id = fields.Many2one('account.payment.term', string='Payment Term', copy=False, tracking=True) parent_id = fields.Many2one('res.partner', string='Related Company', copy=False) blocking_stage = fields.Float(string='Blocking Amount', help="Cannot make sales once the selected " "customer is crossed blocking amount." "Set its value to 0.00 to disable " "this feature", tracking=True, copy=False) warning_stage = fields.Float(string='Warning Amount', help="A warning message will appear once the " "selected customer is crossed warning " "amount. Set its value to 0.00 to" " disable this feature", tracking=True, copy=False) active_limit = fields.Boolean('Active Credit Limit', copy=False, tracking=True) approve_sales_manager = fields.Boolean('Approve Sales Manager', tracking=True, copy=False) approve_finance = fields.Boolean('Approve Finance', tracking=True, copy=False) approve_leader = fields.Boolean('Approve Pimpinan', tracking=True, copy=False) reason = fields.Text('Reason', tracking=True) approve_date = fields.Datetime('Approve Date') state = fields.Selection([ ('waiting_approval_sales_manager', 'Waiting Approval Sales Manager'), ('waiting_approval_finance', 'Waiting Approval Finance'), ('waiting_approval_leader', 'Waiting Approval Leader'), ('approved', 'Approved'), ('rejected', 'Rejected')], default='waiting_approval_sales_manager', tracking=True) reason_reject = fields.Selection([('reason1', 'Reason 1'), ('reason2', 'Reason 2'), ('reason3', 'Reason 3')], string='Reason Reject', tracking=True) reject_reason = fields.Text('Reject Reason', tracking=True) sale_order_ids = fields.Many2many( 'sale.order', string='Sale Orders', copy=False, tracking=True ) total = fields.Char( string='Sale Order Totals', compute='_compute_total' ) grand_total = fields.Float(string='Grand Total', compute="_compute_grand_total") change_log_688 = fields.Text(string="Change Log", readonly=True, copy=False) def write(self, vals): # Ambil nilai lama sebelum perubahan old_values_dict = { rec.id: rec.read(vals.keys())[0] for rec in self } res = super().write(vals) self._track_changes_for_user_688(vals, old_values_dict) return res def _track_changes_for_user_688(self, vals, old_values_dict): # if self.env.user.id != 688: # return tracked_fields = {"blocking_stage", "warning_stage", "property_payment_term_id"} for rec in self: changes = [] old_values = old_values_dict.get(rec.id, {}) for field_name, new_value in vals.items(): if field_name not in tracked_fields: continue field = rec._fields[field_name] old_value = old_values.get(field_name) field_label = field.string # label user-friendly if field.type == 'many2one': old_id = old_value[0] if old_value else False is_different = old_id != new_value if is_different: old_display = old_value[1] if old_value else 'False' new_display = rec.env[field.comodel_name].browse(new_value).display_name if new_value else 'False' changes.append(f"[{field_label}] dari '{old_display}' ke '{new_display}'") else: # Float khusus if field.type == 'float': is_different = not self._float_equal(old_value, new_value) else: is_different = old_value != new_value if is_different: changes.append(f"[{field_label}] dari '{old_value}' ke '{new_value}'") if changes: timestamp = fields.Datetime.now().strftime('%Y-%m-%d %H:%M:%S') user = self.env.user rec.change_log_688 = f"{timestamp} - Perubahan oleh {user.name}:\n" + "\n".join(changes) @staticmethod def _float_equal(val1, val2, eps=1e-6): try: return abs(float(val1 or 0.0) - float(val2 or 0.0)) < eps except Exception: return False def _compute_grand_total(self): for rec in self: grand_total = sum(order.amount_total for order in rec.sale_order_ids) rec.grand_total = grand_total def _compute_total(self): for rec in self: totals_list = [] for order in rec.sale_order_ids: formatted_total = "{:,.2f}".format(order.amount_total) totals_list.append(f"{order.name}: {formatted_total}") rec.total = "\n".join(totals_list) if totals_list else "No Sale Orders" @api.constrains('partner_id') def constrains_partner_id(self): if self.partner_id: self.parent_id = self.partner_id.parent_id.id if self.partner_id.parent_id else None self.blocking_stage = self.partner_id.blocking_stage self.warning_stage = self.partner_id.warning_stage self.active_limit = self.partner_id.active_limit self.property_payment_term_id = self.partner_id.property_payment_term_id.id def button_approve(self): user = self.env.user is_it = user.has_group('indoteknik_custom.group_role_it') if (not user.id ==7 and user.id == 19 and not self.approve_sales_manager) or (is_it and not self.approve_sales_manager): self.approve_sales_manager = True self.state = 'waiting_approval_finance' return if (not user.id ==7 and user.id == 688 and not self.approve_finance) or (is_it and not self.approve_finance): self.approve_finance = True self.state = 'waiting_approval_leader' return if (user.id == 7 and self.approve_finance) or (is_it and not self.approve_leader): self.approve_leader = True if not self.approve_finance and not is_it: raise UserError('Harus Approval Finance!!') if not self.approve_leader and not is_it: raise UserError('Harus Approval Pimpinan!!') if user.id == 7: if not self.approve_finance: raise UserError('Belum Di Approve Oleh Finance') if self.approve_leader == True: self.partner_id.write({ 'blocking_stage': self.blocking_stage, 'warning_stage': self.warning_stage, 'active_limit': self.active_limit, 'property_payment_term_id': self.property_payment_term_id.id, 'is_cbd_locked': False, }) self.approve_date = datetime.utcnow() self.state = 'approved' def button_reject(self): if self.env.user.id not in [688, 7]: raise UserError("Hanya Finance atau Pimpinan Yang Bisa Reject") self.state = 'rejected' @api.model def create(self, vals): vals['number'] = self.env['ir.sequence'].next_by_code('approval.payment.term') or '0' result = super(ApprovalPaymentTerm, self).create(vals) return result