from odoo import models, api, fields, _ from odoo.exceptions import AccessError, UserError, ValidationError from datetime import timedelta, date import logging _logger = logging.getLogger(__name__) class CommissionInternal(models.Model): _name = 'commission.internal' _description = 'Commission Internal' _order = 'date_doc, id desc' _inherit = ['mail.thread'] _rec_name = 'number' number = fields.Char(string='Document No', index=True, copy=False, readonly=True) date_doc = fields.Date(string='Document Date', required=True) month = fields.Selection([ ('01', 'January'), ('02', 'February'), ('03', 'March'), ('04', 'April'), ('05', 'May'), ('06', 'June'), ('07', 'July'), ('08', 'August'), ('09', 'September'), ('10', 'October'), ('11', 'November'), ('12', 'December') ], string="Commission Month") year = fields.Selection([(str(y), str(y)) for y in range(2025, 2036)], string="Commission Year") description = fields.Char(string='Description') comment = fields.Char(string='Comment') commission_internal_line = fields.One2many('commission.internal.line', 'commission_internal_id', string='Lines', auto_join=True, order='account_move_id asc') commission_internal_result = fields.One2many('commission.internal.result', 'commission_internal_id', string='Result', auto_join=True, order='account_move_id asc') @api.model def create(self, vals): vals['number'] = self.env['ir.sequence'].next_by_code('commission.internal') or '0' result = super(CommissionInternal, self).create(vals) return result def _get_commission_internal_bank_account_ids(self): bank_ids = self.env['ir.config_parameter'].sudo().get_param('commission.internal.bank.account.id') if not bank_ids: return [] return [int(x.strip()) for x in bank_ids.split(',') if x.strip().isdigit()] def _get_period_range(self, period_year, period_month): """Return (date_start, date_end) using separate year and month fields.""" self.ensure_one() # make sure it's called on a single record year_str = period_year or '' month_str = period_month or '' # Validate both fields exist if not (year_str.isdigit() and month_str.isdigit()): return None, None year = int(year_str) month = int(month_str) # First day of this month dt_start = date(year, month, 1) # Compute first day of next month if month == 12: next_month = date(year + 1, 1, 1) else: next_month = date(year, month + 1, 1) # Last day = one day before next month's first day dt_end = next_month - timedelta(days=1) return dt_start, dt_end # CREDIT > 0 def _calculate_exclude_credit(self): query = [ ('commission_internal_id.id', '=', self.id), ('credit', '>', 0), ('status', '=', False) ] lines = self.env['commission.internal.line'].search(query) for line in lines: line.helper1 = 'CREDIT' # INV/20 def _calculate_keyword_invoice(self): query = [ ('commission_internal_id.id', '=', self.id), ('helper1', '=', False), ('label', 'ilike', '%INV/20%'), ] lines = self.env['commission.internal.line'].search(query) # parse label and set helper for line in lines: line.helper1 = 'INV/20' # ONGKOS KIRIM SO/20 def _calculate_keyword_deliveryamt(self): query = [ ('commission_internal_id.id', '=', self.id), ('helper1', '=', False), ('label', 'ilike', '%ONGKOS KIRIM SO/20%'), ] lines = self.env['commission.internal.line'].search(query) for line in lines: line.helper1 = 'ONGKOS KIRIM SO/20' # Payment SO/20 def _calculate_keyword_payment(self): query = [ ('commission_internal_id.id', '=', self.id), ('helper1', '=', False), ('label', 'ilike', '%Payment SO/20%'), ] lines = self.env['commission.internal.line'].search(query) for line in lines: line.helper1 = 'Payment SO/20' # UANG MUKA PENJUALAN SO/20 def _calculate_keyword_dp(self): query = [ ('commission_internal_id.id', '=', self.id), ('helper1', '=', False), ('label', 'ilike', '%UANG MUKA PENJUALAN SO/20%'), ] lines = self.env['commission.internal.line'].search(query) for line in lines: line.helper1 = 'UANG MUKA PENJUALAN SO/20' def _calculate_keyword_undefined(self): query = [ ('commission_internal_id.id', '=', self.id), ('helper1', '=', False), ] lines = self.env['commission.internal.line'].search(query) for line in lines: line.helper1 = 'UNDEFINED' def _parse_label_helper2(self): exception = ['CREDIT', 'UNDEFINED'] query = [ ('commission_internal_id.id', '=', self.id), ('helper1', 'not in', exception) ] lines = self.env['commission.internal.line'].search(query) for line in lines: clean_label = line.label.replace('-', ' ').replace(',', ' ') list_label = clean_label.split() list_helper2 = [] for key in list_label: clean_key = key.replace(',', '') # delete commas for make sure if clean_key[:6] == 'INV/20': list_helper2.append(clean_key) elif clean_key[:5] == 'SO/20': list_invoice = self._switch_so_to_inv(clean_key) str_invoice = ' '.join(list_invoice) list_helper2.append(str_invoice) result_helper2 = ' '.join(list_helper2) line.helper2 = result_helper2 def _switch_so_to_inv(self, order): list_state = ['sale', 'done'] result = [] query = [ ('state', 'in', list_state), ('name', '=', order) ] sales = self.env['sale.order'].search(query) if sales: for sale in sales: if sale.invoice_ids: for invoice in sale.invoice_ids: if invoice.state == 'posted': result.append(invoice.name) else: result.append(order) else: result.append(order) return result # fill later TODO @stephan def calculate_commission_internal_result(self): if self.commission_internal_result: raise UserError('Hapus semua isi table result jika ingin di create result ulang') self.message_post(body=("Commission Internal Result generated by %s at %s.") % (self.env.user.name, fields.Datetime.now())) exception = ['ONGKOS KIRIM SO/20'] query = [ ('commission_internal_id.id', '=', self.id), ('helper2', '!=', False), ('helper1', 'not in', exception) ] lines = self.env['commission.internal.line'].search(query) all_invoices_and_sales = [] for line in lines: list_so = list_invoice = [] list_key = line.helper2.split() for key in list_key: if key[:6] == 'INV/20': list_invoice.append(key) if key[:5] == 'SO/20': list_so.append(key) invoices = self.env['account.move'].search([('name', 'in', list_invoice)]) orders = self.env['sale.order'].search([('name', 'in', list_so)]) invoice_data = invoices.mapped(lambda r: { 'res_name': 'account.move', 'res_id': r.id, 'name': r.name, 'date': r.invoice_date, 'customer': r.partner_id.name, 'salesperson': r.user_id.name, 'amount_untaxed': r.amount_untaxed, 'amount_tax': r.amount_tax, 'amount_total': r.amount_total, 'uang_masuk_line_id': line.account_move_line_id.id, 'uang_masuk_id': line.account_move_id.id, 'date_uang_masuk': line.date, 'label_uang_masuk': line.label, 'nomor_uang_masuk': line.number, 'uang_masuk': line.balance, # 'linenetamt_prorate': net_amount_prorate, 'helper1': line.helper1, 'commission_internal_id': line.commission_internal_id.id, 'commission_internal_line_id': line.id, 'helper2': r.state, }) sale_data = orders.mapped(lambda r: { 'res_name': 'sale.order', 'res_id': r.id, 'name': r.name, 'date': r.date_order, 'customer': r.partner_id.name, 'salesperson': r.user_id.name, 'amount_untaxed': r.amount_untaxed, 'amount_tax': r.amount_tax, 'amount_total': r.grand_total, 'uang_masuk_line_id': line.account_move_line_id.id, 'uang_masuk_id': line.account_move_id.id, 'date_uang_masuk': line.date, 'label_uang_masuk': line.label, 'nomor_uang_masuk': line.number, 'uang_masuk': line.balance, # 'linenetamt_prorate': net_amount_prorate, 'helper1': line.helper1, 'commission_internal_id': line.commission_internal_id.id, 'commission_internal_line_id': line.id, 'helper2': r.state, }) invoices_and_sales = invoice_data + sale_data sum_amount_total = sum(item['amount_total'] for item in invoices_and_sales) for item in invoices_and_sales: item['sum_amount_total'] = sum_amount_total all_invoices_and_sales.extend(invoices_and_sales) for data in all_invoices_and_sales: # total_amount = sum(item.get('amount_total', 0.0) for item in invoices_and_sales) # net_amount_prorate = data.get('amount_total', 0.0) * prorate prorate = data.get('amount_total', 0.0) / data.get('sum_amount_total', 0.0) net_amount_prorate = data.get('uang_masuk', 0.0) * prorate self.env['commission.internal.result'].create([{ 'commission_internal_id': data['commission_internal_id'], 'commission_internal_line_id': data['commission_internal_line_id'], 'date_doc': data['date'], 'number': data['name'], 'res_name': data['res_name'], 'res_id': data['res_id'], 'name': data['name'], 'customer': data['customer'], 'salesperson': data['salesperson'], 'totalamt': data['amount_total'], 'uang_masuk_line_id': data['uang_masuk_line_id'], 'uang_masuk_id': data['uang_masuk_id'], 'date_uang_masuk': data['date_uang_masuk'], 'label_uang_masuk': data['label_uang_masuk'], 'nomor_uang_masuk': data['nomor_uang_masuk'], 'uang_masuk': data['uang_masuk'], 'linenetamt_prorate': net_amount_prorate, 'helper1': data['helper1'], 'helper2': data['helper2'] }]) # this button / method works for train data in July 2025 def calculate_commission_internal_from_keyword(self): if not self.commission_internal_line: raise UserError('Commission Internal Line kosong, click Copy GL terlebih dahulu') # execute helper1 for mark keyword self._calculate_exclude_credit() self._calculate_keyword_invoice() self._calculate_keyword_deliveryamt() self._calculate_keyword_payment() self._calculate_keyword_dp() self._calculate_keyword_undefined() # execute helper2 for parse the label into INV/ or SO/ and switch SO to INV if available self._parse_label_helper2() self.message_post(body=("Commission Internal Line calculated by %s at %s.") % (self.env.user.name, fields.Datetime.now())) def generate_commission_from_generate_ledger(self): if self.commission_internal_line: raise UserError('Harus hapus semua line jika ingin generate ulang') if not self.month: raise UserError('Commission Month harus diisi') if not self.year: raise UserError('Commission Year harus diisi') dt_start, dt_end = self._get_period_range(self.year, self.month) account_bank_ids = self._get_commission_internal_bank_account_ids() query = [ ('move_id.state', '=', 'posted'), ('account_id', 'in', account_bank_ids), ('date', '>=', dt_start), ('date', '<=', dt_end) ] ledgers = self.env['account.move.line'].search(query) count = 0 for ledger in ledgers: _logger.info("Read General Ledger Account Move Line %s" % ledger.id) self.env['commission.internal.line'].create([{ 'commission_internal_id': self.id, 'date': ledger.date, 'number': ledger.move_id.name, 'account_move_id': ledger.move_id.id, 'account_move_line_id': ledger.id, 'account_id': ledger.account_id.id, 'label': ledger.name, 'debit': ledger.debit, 'credit': ledger.credit, 'balance': ledger.balance }]) count += 1 self.message_post(body=("Commission Internal Line generated by %s at %s") % (self.env.user.name, fields.Datetime.now())) _logger.info("Commission Internal Line generated %s" % count) class CommissionInternalLine(models.Model): _name = 'commission.internal.line' _description = 'Line' _order = 'number asc, id' commission_internal_id = fields.Many2one('commission.internal', string='Internal Ref', required=True, ondelete='cascade', index=True, copy=False) date = fields.Date(string='Date') number = fields.Char(string='Number') account_move_id = fields.Many2one('account.move', string='Account Move') account_move_line_id = fields.Many2one('account.move.line', string='Account Move Line') account_id = fields.Many2one('account.account', string='Account') label = fields.Char(string='Label') debit = fields.Float(string='Debit') credit = fields.Float(string='Credit') balance = fields.Float(string='Balance') ongkir = fields.Float(string='Ongkir') refund = fields.Float(string='Refund') pph = fields.Float(string='PPh23') others = fields.Float(string='Others') linenetamt = fields.Float(string='Net Amount') dpp = fields.Float(string='DPP') status = fields.Char(string='Status') salespersons = fields.Char(string='Salespersons') invoices = fields.Char(string='Invoices') helper1 = fields.Char(string='Helper1') helper2 = fields.Char(string='Helper2') helper3 = fields.Char(string='Helper3') helper4 = fields.Char(string='Helper4') helper5 = fields.Char(string='Helper5') class CommissionInternalResult(models.Model): _name = 'commission.internal.result' _description = 'Result' _order = 'number asc, id' commission_internal_id = fields.Many2one('commission.internal', string='Internal Ref', required=True, ondelete='cascade', index=True, copy=False) commission_internal_line_id = fields.Many2one('commission.internal.line', string='Line Ref') res_name = fields.Char(string='Res Name') res_id = fields.Integer(string='Res ID') date_doc = fields.Date(string='Date Doc') number = fields.Char(string='Number') name = fields.Char(string='Name') salesperson = fields.Char(string='Salesperson') totalamt = fields.Float(string='Total Amount') uang_masuk_line_id = fields.Many2one('account.move.line', string='Uang Masuk Line ID') uang_masuk_id = fields.Many2one('account.move', string='Uang Masuk ID') date_uang_masuk = fields.Date(string='Date Uang Masuk') label_uang_masuk = fields.Char(string='Label Uang Masuk') nomor_uang_masuk = fields.Char(string='Nomor Uang Masuk') uang_masuk = fields.Float(string='Uang Masuk') ongkir = fields.Float(string='Ongkir') refund = fields.Float(string='Refund') pph = fields.Float(string='PPh23') others = fields.Float(string='Others') linenetamt = fields.Float(string='Net Amount') linenetamt_prorate = fields.Float(string='Net Amount Prorate') dpp = fields.Float(string='DPP') status = fields.Char(string='Status') helper1 = fields.Char(string='Helper1') helper2 = fields.Char(string='Helper2') helper3 = fields.Char(string='Helper3') helper4 = fields.Char(string='Helper4') helper5 = fields.Char(string='Helper5') customer = fields.Char(string='Customer')