diff options
| author | Mqdd <ahmadmiqdad27@gmail.com> | 2026-02-17 16:40:42 +0700 |
|---|---|---|
| committer | Mqdd <ahmadmiqdad27@gmail.com> | 2026-02-17 16:40:42 +0700 |
| commit | 7c893b6cfeae88c82231f682517ae143b4cf580b (patch) | |
| tree | b249c3d3a462d4817a39ff00954765f3a4f7788f | |
| parent | 8bb3696e35dc122ea153566834a48ec0659e0101 (diff) | |
| parent | a461efcda1637c267b3c4195eb0f7d649f10f145 (diff) | |
Merge branch 'odoo-backup' of https://bitbucket.org/altafixco/indoteknik-addons into cr_renca_find
48 files changed, 985 insertions, 123 deletions
diff --git a/indoteknik_api/controllers/api_v1/category.py b/indoteknik_api/controllers/api_v1/category.py index 7d66ad01..4514c2a0 100644 --- a/indoteknik_api/controllers/api_v1/category.py +++ b/indoteknik_api/controllers/api_v1/category.py @@ -250,4 +250,17 @@ class Category(controller.Controller): category = category.parent_frontend_id categories.reverse() - return self.response(categories, headers=[('Cache-Control', 'max-age=3600, public')])
\ No newline at end of file + return self.response(categories, headers=[('Cache-Control', 'max-age=3600, public')]) + + @http.route(prefix + 'category/<int:id>/short-desc', auth='public', methods=['GET', 'OPTIONS']) + @controller.Controller.must_authorized() + def get_category_short_desc(self, id, **kw): + category = request.env['product.public.category'].browse(id) + + if not category.exists(): + return self.response(code=400, description='Category not found') + + return self.response({ + "id": category.id, + "short_desc": category.short_desc or "" + }, headers=[('Cache-Control', 'max-age=600, public')]) diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index dcb23344..ca23995c 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -169,6 +169,7 @@ 'report/report_surat_piutang.xml', 'report/report_tutup_tempo.xml', 'report/purchase_report.xml', + 'report/purchase_report_internal.xml', 'views/vendor_sla.xml', 'views/coretax_faktur.xml', 'views/public_holiday.xml', @@ -192,6 +193,7 @@ 'views/close_tempo_mail_template.xml', 'views/domain_apo.xml', 'views/uom_uom.xml', + 'views/update_depreciation_move_wizard_view.xml', 'views/commission_internal.xml', 'views/keywords.xml' ], diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index 328c76b0..a042750b 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -165,4 +165,5 @@ from . import partial_delivery from . import domain_apo from . import uom_uom from . import commission_internal +from . import update_depreciation_move_wizard from . import keywords diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 37808f94..42467b78 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -49,7 +49,7 @@ class AccountMove(models.Model): states={'draft': [('readonly', False)], 'sent': [('readonly', False)], 'sale': [('readonly', False)]}, domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]", help="Dipakai untuk alamat tempel") - + address_invoice = fields.Char(related='real_invoice_id.street', string='Invoice Address', readonly=True) bills_efaktur_exporter = fields.Many2one('res.users', string='Efaktur Exporter') bills_date_efaktur = fields.Datetime(string="eFaktur Exported Date", required=False) @@ -176,7 +176,7 @@ class AccountMove(models.Model): lambda p: move.id in p.reconciled_invoice_ids.ids ) - if payment: + if payment: move.payment_date = payment[0].date elif move.reklas_misc_id: move.payment_date = move.reklas_misc_id.date @@ -185,7 +185,7 @@ class AccountMove(models.Model): def action_sync_promise_date(self): self.ensure_one() - finance_user_ids = [688] + finance_user_ids = [688] is_it = self.env.user.has_group('indoteknik_custom.group_role_it') if self.env.user.id not in finance_user_ids and not is_it: raise UserError('Hanya Finance (Widya) yang dapat menggunakan fitur ini.') @@ -286,7 +286,7 @@ class AccountMove(models.Model): if all(inv.reminder_sent_date == today for inv in invs): _logger.info(f"Reminder untuk {partner.name} sudah terkirim hari ini, skip.") continue - + promise_dates = [inv.customer_promise_date for inv in invs if inv.customer_promise_date] if promise_dates: earliest_promise = min(promise_dates) # ambil janji paling awal @@ -300,11 +300,11 @@ class AccountMove(models.Model): # skip semua jika partner centang dont_send_reminder_inv_all if partner.dont_send_reminder_inv_all: _logger.info(f"Partner {partner.name} skip karena dont_send_reminder_inv_all aktif") - continue + continue # cek parent hanya dengan flag dont_sent_reminder_inv_parent if not partner.dont_send_reminder_inv_parent and partner.email: emails.append(partner.email) - + # Ambil child contact yang di-checklist reminder_invoices reminder_contacts = self.env['res.partner'].search([ ('parent_id', '=', partner.id), @@ -312,7 +312,7 @@ class AccountMove(models.Model): ('email', '!=', False), ]) _logger.info(f"Email Reminder Child {reminder_contacts}") - + emails += reminder_contacts.mapped('email') if reminder_contacts: _logger.info(f"Email Reminder Child {reminder_contacts}") @@ -381,7 +381,7 @@ class AccountMove(models.Model): # tempo_link = 'http://localhost:2100/my/tempo' # payment_term = partner.previous_payment_term_id if partner.is_cbd_locked else partner.property_payment_term_id payment_term = invs[0].invoice_payment_term_id - + limit_info_html = f""" <p><b>Informasi Tambahan:</b></p> <ul style="font-size:12px; color:#333; line-height:1.4; margin:0; padding-left:0; list-style-position:inside;"> @@ -391,11 +391,11 @@ class AccountMove(models.Model): Sisa Kredit Limit: {formatLang(self.env, blocking_limit - outstanding_amount, currency_obj=currency)} </li> <li style="color:red;"> - Kredit Limit Terpakai: {formatLang(self.env, outstanding_amount, currency_obj=currency)} + Kredit Limit Terpakai: {formatLang(self.env, outstanding_amount, currency_obj=currency)} <span style="font-size:12px; color:#666;">({len(outstanding_invoices)} Transaksi)</span> </li> <li style="color:red;"> - Jatuh Tempo: {formatLang(self.env, overdue_amount, currency_obj=currency)} + Jatuh Tempo: {formatLang(self.env, overdue_amount, currency_obj=currency)} <span style="font-size:12px; color:#666;">({len(overdue_invoices)} Invoice)</span> </li> </ul> @@ -472,7 +472,7 @@ class AccountMove(models.Model): values = { 'subject': f"Reminder Invoice Due - {partner.name}", # 'email_to': 'andrifebriyadiputra@gmail.com', - 'email_to': email_to, + 'email_to': email_to, 'email_from': 'finance@indoteknik.co.id', 'email_cc': ",".join(sorted(set(cc_list))), 'body_html': body_html, @@ -528,7 +528,7 @@ class AccountMove(models.Model): # payment_term = rec.invoice_payment_term_id.line_ids[0].days # terima_faktur = rec.date_terima_tukar_faktur # payment = self.search([('ref', '=', rec.name), ('move_type', '=', 'entry')], limit=1) - + # if payment and terima_faktur: # date_diff = terima_faktur - payment.date # rec.length_of_payment = date_diff.days + payment_term @@ -587,14 +587,30 @@ class AccountMove(models.Model): records = self.search([('id', 'in', self.ids)]) template = self.env.ref('indoteknik_custom.mail_template_efaktur_document') + ICP = self.env['ir.config_parameter'].sudo() + special_partner_ids = set( + int(x) for x in (ICP.get_param('efaktur.special_partner_ids') or '').split(',') if x + ) for record in records: - if record.invoice_payment_term_id.id == 26: + if record.invoice_payment_term_id.id == 26: attachment = self.generate_attachment(record) email_values = { 'attachment_ids': [(4, attachment.id)] } template.send_mail(record.id, email_values=email_values, force_send=True) + + elif record.partner_id.id in special_partner_ids: + attachment = self.generate_attachment(record) + email_list = [record.partner_id.email] if record.partner_id.email else [] + if record.real_invoice_id and record.real_invoice_id.email: + email_list.append(record.real_invoice_id.email) + + email_values = { + 'email_to': ",".join(set(email_list)), + 'attachment_ids': [(4, attachment.id)] + } + template.send_mail(record.id, email_values=email_values, force_send=True) # @api.model # def create(self, vals): @@ -605,7 +621,7 @@ class AccountMove(models.Model): @api.model def create(self, vals): - vals['nomor_kwitansi'] = self.env['ir.sequence'].next_by_code('nomor.kwitansi') or '0' + vals['nomor_kwitansi'] = self.env['ir.sequence'].next_by_code('nomor.kwitansi') or '0' result = super(AccountMove, self).create(vals) # Tambahan: jika ini Vendor Bill dan tanggal belum diisi @@ -640,13 +656,13 @@ class AccountMove(models.Model): record.flag_delivery_amt = True else: record.flag_delivery_amt = False - + def compute_delivery_amt_text(self): tb = Terbilang() for record in self: res = '' - + try: if record.sale_id.delivery_amt > 0: tb.parse(int(record.sale_id.delivery_amt)) @@ -654,7 +670,7 @@ class AccountMove(models.Model): record.delivery_amt_text = res + ' Rupiah' except: record.delivery_amt_text = res - + @api.constrains('bills_efaktur_document') def _constrains_efaktur_document(self): @@ -722,13 +738,35 @@ class AccountMove(models.Model): raise UserError('Data Hanya Bisa Di Cancel') return res + def copy(self, default=None): + default = dict(default or {}) + + today = fields.Date.context_today(self) + + if self.invoice_date_due: + default.update({ + 'invoice_date_due': today, + }) + + move = super().copy(default) + + for line in move.line_ids: + if ( + line.account_id.user_type_id.type in ('receivable', 'payable') + and line.date_maturity + ): + line.date_maturity = today + + return move + + def button_cancel(self): res = super(AccountMove, self).button_cancel() if self.move_type == 'entry': po = self.env['purchase.order'].search([ ('move_id', 'in', [self.id]) ]) - + for order in po: if order: order.is_create_uangmuka = False @@ -741,7 +779,7 @@ class AccountMove(models.Model): res = super(AccountMove, self).button_draft() if not self.env.user.is_accounting: raise UserError('Hanya Accounting yang bisa Reset to Draft') - + for rec in self.line_ids: if rec.write_date != rec.create_date: if rec.statement_line_id and not rec.statement_line_id.statement_id.is_edit and rec.statement_line_id.statement_id.state == 'confirm': @@ -751,7 +789,7 @@ class AccountMove(models.Model): def action_post(self): if self._name != 'account.move': return super(AccountMove, self).action_post() - + # validation cant qty invoice greater than qty order if self.move_type == 'out_invoice': query = ["&",("name","=",self.invoice_origin),"|",("state","=","sale"),("state","=","done")] @@ -777,7 +815,7 @@ class AccountMove(models.Model): # raise UserError('Hanya Accounting yang bisa Posting') # if self._name == 'account.move': # for entry in self: - # entry.date_completed = datetime.utcnow() + # entry.date_completed = datetime.utcnow() # for line in entry.line_ids: # line.date_maturity = entry.date @@ -787,7 +825,7 @@ class AccountMove(models.Model): if not self.env.user.is_accounting: raise UserError("Hanya Finence yang bisa ubah data") return res - + def _compute_invoice_day_to_due(self): for invoice in self: invoice_day_to_due = 0 @@ -801,7 +839,7 @@ class AccountMove(models.Model): new_invoice_day_to_due = new_invoice_day_to_due.days invoice.invoice_day_to_due = invoice_day_to_due invoice.new_invoice_day_to_due = new_invoice_day_to_due - + def _compute_bill_day_to_due(self): for rec in self: rec.bill_day_to_due = rec.payment_schedule or rec.invoice_date_due @@ -831,21 +869,21 @@ class AccountMove(models.Model): add_days += line.days due_date = tukar_date + timedelta(days=add_days) invoice.invoice_date_due = due_date - + def open_form_multi_update(self): action = self.env['ir.actions.act_window']._for_xml_id('indoteknik_custom.action_account_move_multi_update') action['context'] = { 'move_ids': [x.id for x in self] } return action - + def open_form_multi_update_bills(self): action = self.env['ir.actions.act_window']._for_xml_id('indoteknik_custom.action_account_move_multi_update_bills') action['context'] = { 'move_ids': [x.id for x in self] } return action - + @api.constrains('efaktur_id', 'ref', 'date', 'journal_id', 'name') def constrains_edit(self): for rec in self.line_ids: @@ -859,10 +897,10 @@ class AccountMove(models.Model): # if rec.statement_line_id and not rec.statement_line_id.statement_id.is_edit and rec.statement_line_id.statement_id.state == 'confirm': # raise UserError('Bank Statement di Lock, Minta admin reconcile untuk unlock') # return res - + def validate_faktur_for_export(self): - invoices = self.filtered(lambda inv: not inv.is_efaktur_exported and - inv.state == 'posted' and + invoices = self.filtered(lambda inv: not inv.is_efaktur_exported and + inv.state == 'posted' and inv.move_type == 'out_invoice') invalid_invoices = self - invoices @@ -874,10 +912,10 @@ class AccountMove(models.Model): )) return invoices - + def export_faktur_to_xml(self): valid_invoices = self - + coretax_faktur = self.env['coretax.faktur'].create({}) response = coretax_faktur.export_to_download( diff --git a/indoteknik_custom/models/account_move_line.py b/indoteknik_custom/models/account_move_line.py index 7c95d4ef..5edea25f 100644 --- a/indoteknik_custom/models/account_move_line.py +++ b/indoteknik_custom/models/account_move_line.py @@ -1,5 +1,5 @@ from odoo import models, api, fields - +from odoo.exceptions import AccessError, UserError, ValidationError class AccountMoveLine(models.Model): _inherit = "account.move.line" @@ -9,6 +9,69 @@ class AccountMoveLine(models.Model): analytic_account_ids = fields.Many2many('account.analytic.account', string='Analytic Account') line_no = fields.Integer('No', default=0) + def action_gl_reconcile(self): + lines = self + + journal = self.env['account.journal'].search([ + ('suspense_account_id', '=', lines[0].account_id.id) + ], limit=1) + + if not journal: + raise UserError('Journal dengan suspense account ini tidak ditemukan!') + + statement = self.env['account.bank.statement'].create({ + 'journal_id': journal.id, + 'name': f'REKONSIL {journal.name} {lines[0].date.strftime("%d-%m-%Y")}', + 'company_id': self.env.company.id, + 'date': lines[0].date, + }) + + widget_vals = [] + st_line_ids = [] + + for line in lines: + amount = line.debit - line.credit + + st_line = self.env['account.bank.statement.line'].create({ + 'statement_id': statement.id, + 'date': line.date or fields.Date.today(), + 'payment_ref': line.name, + 'partner_id': line.partner_id.id, + 'amount': amount, + 'ref': line.name, + }) + + st_line_ids.append(st_line.id) + + widget_vals.append({ + 'partner_id': st_line.partner_id.id, + 'counterpart_aml_dicts': [{ + 'counterpart_aml_id': line.id, + 'debit': abs(amount) if amount < 0 else 0, + 'credit': abs(amount) if amount > 0 else 0, + 'name': line.name or '/', + }], + 'payment_aml_ids': [], + 'new_aml_dicts': [], + 'to_check': False, + }) + + statement.button_post() + + self.env['account.reconciliation.widget'].process_bank_statement_line( + st_line_ids, + widget_vals + ) + # statement.button_validate_or_action() + + return { + 'effect': { + 'fadeout': 'slow', + 'message': 'Statement + Auto Reconcile sukses besar 😎🔥', + 'type': 'rainbow_man', + } + } + @api.onchange('account_id') def _onchange_account_id(self): for account in self: diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py index f973a9da..8cadb1b6 100644 --- a/indoteknik_custom/models/advance_payment_request.py +++ b/indoteknik_custom/models/advance_payment_request.py @@ -55,12 +55,14 @@ class AdvancePaymentRequest(models.Model): ('pengajuan2', 'Menunggu Approval AP'), ('pengajuan3', 'Menunggu Approval Pimpinan'), ('approved', 'Approved'), + ('cancel','Cancel') ], string='Status', default='draft', tracking=3, index=True, track_visibility='onchange') status_pay_down_payment = fields.Selection([ ('pending', 'Pending'), ('payment', 'Payment'), + ('cancel','Cancel') ], string='Status Pembayaran', default='pending', tracking=3) name_approval_departement = fields.Char(string='Approval Departement') @@ -639,10 +641,16 @@ class AdvancePaymentRequest(models.Model): today = date.today() for rec in self: - current_days = rec.days_remaining or 0 - current_due_date = rec.estimated_return_date or False - if rec.type_request == 'pum': - is_settlement_approved = any(s.status == 'approved' for s in rec.settlement_ids) + # current_days = rec.days_remaining or 0 + # current_due_date = rec.estimated_return_date or False + current_days = 0 + current_due_date = False + + is_settlement_approved = any(s.status == 'approved' for s in rec.settlement_ids) + is_pum_canceled = (rec.status == 'cancel') + + if rec.type_request == 'pum' and not is_pum_canceled and not is_settlement_approved: + if not is_settlement_approved: due_date = False @@ -816,6 +824,52 @@ class AdvancePaymentRequest(models.Model): rec._compute_grand_total_reimburse() rec.nominal = rec.grand_total_reimburse return rec + + def action_open_cancel_wizard(self): + """Membuka Wizard Pop-up untuk Cancel PUM/Reimburse""" + self.ensure_one() + + if self.move_id: + raise UserError(_("Pengajuan tidak dapat dibatalkan karena Journal sudah terbentuk.")) + + if self.settlement_ids and any(s.status != 'draft' for s in self.settlement_ids): + raise UserError(_("Pengajuan tidak dapat dibatalkan karena sudah ada proses realisasi.")) + + return { + 'name': _('Alasan Pembatalan'), + 'type': 'ir.actions.act_window', + 'res_model': 'advance.payment.cancel.wizard', + 'view_mode': 'form', + 'target': 'new', + 'context': { + 'default_request_id': self.id, + } + } + +class AdvancePaymentCancelWizard(models.TransientModel): + _name = 'advance.payment.cancel.wizard' + _description = 'Wizard untuk Membatalkan PUM/Reimburse' + + request_id = fields.Many2one('advance.payment.request', string='Dokumen', readonly=True) + reason = fields.Text(string='Alasan Pembatalan', required=True) + + def action_confirm_cancel(self): + self.ensure_one() + request = self.request_id + if request.move_id: + raise UserError("Tidak bisa melakukan cancel karena Jurnal (Move ID) sudah terbentuk.") + + request.write({'status': 'cancel'}) + request.write({'status_pay_down_payment': 'cancel'}) + + request.message_post( + body=f"Pengajuan telah <b>DIBATALKAN</b> oleh {self.env.user.name}.<br/>" + f"<b>Alasan:</b> {self.reason}", + message_type="comment", + subtype_xmlid="mail.mt_note", + ) + + return {'type': 'ir.actions.act_window_close'} class AdvancePaymentUsageLine(models.Model): diff --git a/indoteknik_custom/models/automatic_purchase.py b/indoteknik_custom/models/automatic_purchase.py index 4b0ce325..0b2f7d1b 100644 --- a/indoteknik_custom/models/automatic_purchase.py +++ b/indoteknik_custom/models/automatic_purchase.py @@ -499,7 +499,8 @@ class AutomaticPurchase(models.Model): # _logger.info('test %s' % point.product_id.name) if point.product_id.qty_available_bandengan > point.product_min_qty: continue - qty_purchase = point.product_max_qty - point.product_id.qty_incoming_bandengan - point.product_id.qty_available_bandengan + # qty_purchase = point.product_max_qty - point.product_id.qty_incoming_bandengan - point.product_id.qty_available_bandengan + qty_purchase = point.product_max_qty - point.product_id.qty_available_bandengan po_line = self.env['purchase.order.line'].search([('product_id', '=', point.product_id.id), ('order_id.state', '=', 'done')], order='id desc', limit=1) if self.vendor_id: diff --git a/indoteknik_custom/models/commision.py b/indoteknik_custom/models/commision.py index 441dd54f..983c07fe 100644 --- a/indoteknik_custom/models/commision.py +++ b/indoteknik_custom/models/commision.py @@ -489,7 +489,10 @@ class CustomerCommision(models.Model): raise UserError('Line sudah ada, tidak bisa di generate ulang') if self.commision_type == 'fee': - self._generate_customer_commision_fee() + if self.brand_ids: + self._generate_customer_commision_rebate() + else: + self._generate_customer_commision_fee() else: self._generate_customer_commision_rebate() diff --git a/indoteknik_custom/models/price_group.py b/indoteknik_custom/models/price_group.py index 1b1c817f..fce78fff 100644 --- a/indoteknik_custom/models/price_group.py +++ b/indoteknik_custom/models/price_group.py @@ -18,6 +18,8 @@ class PriceGroup(models.Model): group6 = fields.Float(string='Kelompok 6 (%)') group7 = fields.Float(string='Kelompok 7 (%)') group8 = fields.Float(string='Kelompok 8 (%)') + group9 = fields.Float(string='Kelompok 9 (%)') + group10 = fields.Float(string='Kelompok 10 (%)') def collect_price_group(self): PRICE_GROUP_ID = { @@ -47,4 +49,6 @@ class Manufacture(models.Model): ('group6', 'Kelompok 6'), ('group7', 'Kelompok 7'), ('group8', 'Kelompok 8'), + ('group9', 'Kelompok 9'), + ('group10', 'Kelompok 10'), ], string='Pricing Group', copy=False) diff --git a/indoteknik_custom/models/product_public_category.py b/indoteknik_custom/models/product_public_category.py index 1039ec52..dadcb181 100755 --- a/indoteknik_custom/models/product_public_category.py +++ b/indoteknik_custom/models/product_public_category.py @@ -15,6 +15,7 @@ class ProductPublicCategory(models.Model): child_frontend_id = fields.One2many('product.public.category', 'parent_frontend_id', string='Children Frontend Categories') child_frontend_id2 = fields.Many2many('product.public.category', relation='website_categories_child_frontend_rel', column1='website_categories_homepage_id', column2='product_public_category_id', string='Category Level 3') sequence_frontend = fields.Integer(help="Gives the sequence order when displaying a list of product categories.", index=True) + short_desc = fields.Char(string='Short Description') # @api.model # def _onchange_child_frontend_id2(self, parent): diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py index 75adb352..397bd06d 100755 --- a/indoteknik_custom/models/product_template.py +++ b/indoteknik_custom/models/product_template.py @@ -18,13 +18,13 @@ class ProductTemplate(models.Model): x_attribute_set_id = fields.Integer( string="Magento Attribute Set ID", - default=0, - index=True, - help="Attribute Set ID dari Magento" + help="Attribute Set ID dari Magento", + readonly=True ) x_attribute_set_name = fields.Char( string="Magento Attribute Set Name", - help="Attribute Set Name dari Magento" + help="Attribute Set Name dari Magento", + readonly=True ) image_carousel_lines = fields.One2many( @@ -85,7 +85,6 @@ class ProductTemplate(models.Model): merchandise_ok = fields.Boolean(string='Product Promotion') print_barcode = fields.Boolean(string='Print Barcode', default=True) # qr_code = fields.Binary("QR Code", compute='_compute_qr_code') - # has_magento = fields.Boolean(string='Has Magento?', default=False) @api.model def create(self, vals): @@ -967,6 +966,8 @@ class ProductProduct(models.Model): return True + has_magento = fields.Boolean(string='Has Magento?', default=False, readonly=True) + def generate_product_sla(self): product_variant_ids = self.env.context.get('active_ids', []) product_variant = self.search([('id', 'in', product_variant_ids)]) diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 3d22b0f0..b3ecca56 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -53,6 +53,7 @@ class PurchaseOrder(models.Model): total_so_percent_margin = fields.Float( 'SO Margin%', compute='compute_total_margin', help="Total % Margin in Sales Order Header") + amount_cashback = fields.Float('Cashback', compute = 'compute_total_margin', help = 'Total Cashback brand Altama') amount_total_without_service = fields.Float('AmtTotalWithoutService', compute='compute_amt_total_without_service') summary_qty_po = fields.Float('Total Qty', compute='_compute_summary_qty') summary_qty_receipt = fields.Float('Summary Qty Receipt', compute='_compute_summary_qty') @@ -1081,18 +1082,11 @@ class PurchaseOrder(models.Model): def button_confirm(self): if self.env.user.id != 7 and not self.env.user.is_leader: # Pimpinan if '/PJ/' in self.name: - low_margin_lines = self.order_sales_match_line.filtered( - lambda match: match.so_header_margin <= 15.0 - ) price_change_detected = any(line.price_unit_before for line in self.order_line) - if low_margin_lines and price_change_detected: - # raise UserError("Matches SO terdapat item dengan header margin SO <= 15%. Approval Pimpinan diperlukan.") - raise UserError("Approval Pimpinan diperlukan jika terdapat perubahan Unit Price pada PO Line yang Matches SO item memiliki header margin SO <= 15%") - # else: - # is_po_manual = '/A/' not in self.name and '/MO/' not in self.name - # if is_po_manual: - # if not self.order_sales_match_line: - # raise UserError("Tidak ada matches SO, Approval Pimpinan diperlukan.") + if price_change_detected: + if self.order_sales_match_line: + if self.total_percent_margin <= 15.0: + raise UserError("Approval Pimpinan diperlukan jika terdapat perubahan Unit Price pada PO Line dan Memiliki Margin <= 15%") self._check_assets_note() # self._check_payment_term() # check payment term @@ -1417,19 +1411,36 @@ class PurchaseOrder(models.Model): real_item_margin = sales_price - purchase_price sum_margin += real_item_margin + cashback_amount = 0 + if self.partner_id.id == 5571: + cashback_percent = line.product_id.x_manufacture.cashback_percent or 0.0 + if cashback_percent > 0: + cashback_amount = purchase_price * cashback_percent + purchase_price -= cashback_amount + + # line.amount_cashback = cashback_amount + if sum_so_margin != 0 and sum_sales_price != 0 and sum_margin != 0: self.total_so_margin = sum_so_margin self.total_so_percent_margin = round((sum_so_margin / sum_sales_price), 2) * 100 self.total_margin = sum_margin self.total_percent_margin = round((sum_margin / sum_sales_price), 2) * 100 + self.amount_cashback = 0 + elif self.partner_id.id == 5571 and sum_so_margin != 0 and sum_sales_price != 0 and sum_margin != 0 and cashback_amount != 0: + self.total_so_margin = sum_so_margin + self.total_so_percent_margin = round((sum_so_margin / sum_sales_price), 2) * 100 + self.total_margin = sum_margin + self.total_percent_margin = round((sum_margin / sum_sales_price), 2) * 100 + self.amount_cashback = cashback_amount else: self.total_margin = 0 self.total_percent_margin = 0 self.total_so_margin = 0 self.total_so_percent_margin = 0 + self.amount_cashback = 0 def compute_total_margin_from_apo(self): - sum_so_margin = sum_sales_price = sum_margin = 0 + sum_so_margin = sum_sales_price = sum_margin = cashback_amount = 0 for line in self.order_sales_match_line: po_line = self.env['purchase.order.line'].search([ ('product_id', '=', line.product_id.id), @@ -1466,18 +1477,27 @@ class PurchaseOrder(models.Model): if line.purchase_order_id.delivery_amt > 0: purchase_price += line.purchase_order_id.delivery_amt + if self.partner_id.id == 5571: + cashback_percent = line.product_id.x_manufacture.cashback_percent or 0.0 + if cashback_percent > 0: + cashback_amount = purchase_price * cashback_percent + purchase_price -= cashback_amount + real_item_margin = sales_price - purchase_price sum_margin += real_item_margin + self.amount_cashback = cashback_amount # Akumulasi hasil akhir if sum_sales_price != 0: self.total_so_margin = sum_so_margin self.total_so_percent_margin = round((sum_so_margin / sum_sales_price), 2) * 100 self.total_margin = sum_margin self.total_percent_margin = round((sum_margin / sum_sales_price), 2) * 100 + self.amount_cashback = cashback_amount else: self.total_margin = self.total_percent_margin = 0 self.total_so_margin = self.total_so_percent_margin = 0 + self.amount_cashback = 0 def compute_amt_total_without_service(self): diff --git a/indoteknik_custom/models/purchase_order_line.py b/indoteknik_custom/models/purchase_order_line.py index 8c72887d..76dcc09e 100755 --- a/indoteknik_custom/models/purchase_order_line.py +++ b/indoteknik_custom/models/purchase_order_line.py @@ -23,6 +23,9 @@ class PurchaseOrderLine(models.Model): so_item_percent_margin = fields.Float( 'SO Margin%', compute='compute_item_margin', help="Total % Margin in Sales Order Header") + amount_cashback = fields.Float( + 'SO Margin%', compute='_compute_cashback_brand', + help="Total % Margin in Sales Order Header") delivery_amt_line = fields.Float('DeliveryAmtLine', compute='compute_delivery_amt_line') line_no = fields.Integer('No', default=0) qty_available = fields.Float('Qty Available', compute='_compute_qty_stock') @@ -373,6 +376,9 @@ class PurchaseOrderLine(models.Model): purchase_price = line.price_subtotal if order.delivery_amount > 0: purchase_price += line.delivery_amt_line + + if line.amount_cashback > 0: + purchase_price = purchase_price - line.amount_cashback # Hitung margin dan persentase margin real_item_margin = total_sales_price - purchase_price @@ -384,6 +390,46 @@ class PurchaseOrderLine(models.Model): sum_margin += real_item_margin + def _compute_cashback_brand(self): + start_date = datetime(2026, 2, 1, 0, 0, 0) + + for line in self: + line.amount_cashback = 0.0 + + product = line.product_id + order = line.order_id + + if not product or not order: + continue + + if order.partner_id.id != 5571: + continue + + sales_matches = self.env['purchase.order.sales.match'].search([ + ('purchase_order_id', '=', order.id), + ('product_id', '=', product.id) + ]) + + total_cashback = 0.0 + + for match in sales_matches: + so_line = match.sale_line_id + so_order = so_line.order_id + + if not so_order.date_order or so_order.date_order < start_date: + continue + + cashback_percent = product.x_manufacture.cashback_percent or 0.0 + if cashback_percent <= 0: + continue + sales_price = so_line.price_reduce_taxexcl * match.qty_so + + cashback = sales_price * cashback_percent + total_cashback += cashback + + line.amount_cashback = total_cashback + + def compute_delivery_amt_line(self): for line in self: if line.product_id.type == 'product': diff --git a/indoteknik_custom/models/purchasing_job.py b/indoteknik_custom/models/purchasing_job.py index 7a7e70b4..e457b86c 100644 --- a/indoteknik_custom/models/purchasing_job.py +++ b/indoteknik_custom/models/purchasing_job.py @@ -24,7 +24,8 @@ class PurchasingJob(models.Model): ('apo', 'APO') ], string='APO?') purchase_representative_id = fields.Many2one('res.users', string="Purchase Representative", readonly=True) - note = fields.Char(string="Note Detail") + note = fields.Char(string="Note") + note_detail = fields.Text(string="Note Detail") date_po = fields.Datetime(string='Date PO', copy=False) so_number = fields.Text(string='SO Number', copy=False) check_pj = fields.Boolean(compute='_get_check_pj', string='Linked') @@ -126,6 +127,7 @@ class PurchasingJob(models.Model): pmp.action, max(pjs.status_apo::text) AS status_apo, max(pjs.note::text) AS note, + max(pjs.note_detail::text) AS note_detail, max(pjs.date_po::text) AS date_po, pmp.so_number, CASE diff --git a/indoteknik_custom/models/purchasing_job_multi_update.py b/indoteknik_custom/models/purchasing_job_multi_update.py index 80a43e45..0eca499f 100644 --- a/indoteknik_custom/models/purchasing_job_multi_update.py +++ b/indoteknik_custom/models/purchasing_job_multi_update.py @@ -20,10 +20,19 @@ class PurchasingJobMultiUpdate(models.TransientModel): # purchasing_job_state.unlink() - purchasing_job_state.create({ - 'purchasing_job_id': product.id, - 'status_apo': 'apo', - }) + vals = { + 'purchasing_job_id': product.id, # Pastikan ID ini benar mapping ke ID produk + 'status_apo': 'apo', + } + + if purchasing_job_state: + purchasing_job_state.write(vals) + else: + purchasing_job_state.create(vals) + # purchasing_job_state.create({ + # 'purchasing_job_id': product.id, + # 'status_apo': 'apo', + # }) apo = self.env['automatic.purchase'].generate_regular_purchase(products) return { diff --git a/indoteknik_custom/models/purchasing_job_state.py b/indoteknik_custom/models/purchasing_job_state.py index d014edfe..ca557de1 100644 --- a/indoteknik_custom/models/purchasing_job_state.py +++ b/indoteknik_custom/models/purchasing_job_state.py @@ -14,5 +14,6 @@ class PurchasingJobState(models.Model): ('not_apo', 'Belum APO'), ('apo', 'APO') ], string='APO?', copy=False) - note = fields.Char(string="Note Detail", copy=False) + note = fields.Char(string="Note", copy=False) + note_detail = fields.Text(string="Note Detail", copy=False) date_po = fields.Datetime(string='Date PO', copy=False) diff --git a/indoteknik_custom/models/refund_sale_order.py b/indoteknik_custom/models/refund_sale_order.py index c22e84ab..4c3ca52e 100644 --- a/indoteknik_custom/models/refund_sale_order.py +++ b/indoteknik_custom/models/refund_sale_order.py @@ -62,7 +62,8 @@ class RefundSaleOrder(models.Model): ('uang', 'Refund Lebih Bayar'), ('retur_half', 'Refund Retur Sebagian'), ('retur', 'Refund Retur Full'), - ('salah_transfer', 'Salah Transfer') + ('salah_transfer', 'Salah Transfer'), + ('berita_acara', 'Kebutuhan Berita Acara') ], string='Refund Type', required=True) tukar_guling_ids = fields.One2many( @@ -242,7 +243,7 @@ class RefundSaleOrder(models.Model): ) invoices = sale_orders.mapped('invoice_ids').filtered( - lambda inv: inv.move_type in ['out_invoice', 'out_refund'] and inv.state != 'cancel' + lambda inv: inv.move_type in ['out_invoice', 'out_refund'] and inv.state == 'posted' ) if invoices: vals['invoice_ids'] = [(6, 0, invoices.ids)] @@ -251,7 +252,7 @@ class RefundSaleOrder(models.Model): invoice_ids_data = vals.get('invoice_ids', []) invoice_ids = invoice_ids_data[0][2] if invoice_ids_data and invoice_ids_data[0][0] == 6 else [] invoices = self.env['account.move'].browse(invoice_ids) - if invoice_ids and refund_type and refund_type not in ['uang', 'barang_kosong_sebagian', 'barang_kosong', 'retur_half']: + if invoice_ids and refund_type and refund_type not in ['uang', 'barang_kosong_sebagian', 'barang_kosong', 'retur_half', 'berita_acara']: raise UserError("Refund type Hanya Bisa Lebih Bayar, Barang Kosong Sebagian, atau Retur jika ada invoice") if not invoice_ids and refund_type and refund_type in ['uang', 'barang_kosong_sebagian', 'retur_half']: @@ -296,6 +297,7 @@ class RefundSaleOrder(models.Model): ]) piutangbca = self.env['account.move'] piutangmdr = self.env['account.move'] + cabinvoice = self.env['account.move'] for inv_name in invoices.mapped('name'): piutangbca |= self.env['account.move'].search([ @@ -308,6 +310,11 @@ class RefundSaleOrder(models.Model): ('journal_id', '=', 7), ('state', '=', 'posted'), ]) + cabinvoice |= self.env['account.move'].search([ + ('ref', 'ilike', inv_name), + ('journal_id', '=', 11), + ('state', '=', 'posted'), + ]) misc = self.env['account.move'] if invoices: @@ -378,6 +385,7 @@ class RefundSaleOrder(models.Model): has_moves3 = bool(moves3) has_piutangmdr = bool(piutangmdr) has_piutangbca = bool(piutangbca) + has_cabinvoice = bool(cabinvoice) has_misc = bool(misc) has_ongkir = bool(moves_ongkir) ssos = self.env['sale.order'].browse(so_ids) @@ -385,7 +393,7 @@ class RefundSaleOrder(models.Model): sisa_uang_masuk = 0.0 - has_journal = has_moves or has_moves2 or has_moves3 or has_piutangbca or has_piutangmdr or has_misc + has_journal = has_moves or has_moves2 or has_moves3 or has_piutangbca or has_piutangmdr or has_misc or has_cabinvoice if has_moves: sisa_uang_masuk += sum(moves.mapped('amount_total_signed')) @@ -397,6 +405,8 @@ class RefundSaleOrder(models.Model): sisa_uang_masuk += sum(moves3.mapped('amount_total_signed')) if has_piutangbca: sisa_uang_masuk += sum(piutangbca.mapped('amount_total_signed')) + if has_cabinvoice: + sisa_uang_masuk += sum(cabinvoice.mapped('amount_total_signed')) if has_piutangmdr: sisa_uang_masuk += sum(piutangmdr.mapped('amount_total_signed')) if has_misc: @@ -425,13 +435,17 @@ class RefundSaleOrder(models.Model): total_invoice = sum(self.env['account.move'].browse(invoice_ids).mapped('amount_total_signed')) if invoice_ids else 0.0 vals['total_invoice'] = total_invoice amount_refund = vals.get('amount_refund', 0.0) - can_refund = sisa_uang_masuk - total_invoice - - if amount_refund > can_refund or can_refund == 0.0: - raise ValidationError( - _("Maksimal refund yang bisa dilakukan adalah sebesar %s. " - "Silakan sesuaikan jumlah refund.") % (can_refund) - ) + can_refund = 0.0 + if refund_type == 'berita_acara': + can_refund = sisa_uang_masuk + else: + can_refund = sisa_uang_masuk - total_invoice + if refund_type != 'berita_acara': + if amount_refund > can_refund or can_refund == 0.0: + raise ValidationError( + _("Maksimal refund yang bisa dilakukan adalah sebesar %s. " + "Silakan sesuaikan jumlah refund.") % (can_refund) + ) if amount_refund <= 0.00: raise ValidationError('Total Refund harus lebih dari 0 jika ingin mengajukan refund') @@ -483,7 +497,7 @@ class RefundSaleOrder(models.Model): valid_invoices = sale_orders.mapped('invoice_ids').filtered( - lambda inv: inv.move_type in ['out_invoice', 'out_refund'] and inv.state != 'cancel' + lambda inv: inv.move_type in ['out_invoice', 'out_refund'] and inv.state == 'posted' ) vals['invoice_ids'] = [(6, 0, valid_invoices.ids)] vals['ongkir'] = sum(so.delivery_amt or 0.0 for so in sale_orders) @@ -526,7 +540,7 @@ class RefundSaleOrder(models.Model): else: invoice_ids = rec.invoice_ids.ids - if invoice_ids and vals.get('refund_type', rec.refund_type) not in ['uang', 'barang_kosong_sebagian', 'barang_kosong', 'retur_half', 'retur']: + if invoice_ids and vals.get('refund_type', rec.refund_type) not in ['uang', 'barang_kosong_sebagian', 'barang_kosong', 'retur_half', 'retur', 'berita_acara']: raise UserError("Refund type Hanya Bisa Lebih Bayar, Barang Kosong Sebagian, atau Retur jika ada invoice") if not invoice_ids and vals.get('refund_type', rec.refund_type) in ['uang', 'barang_kosong_sebagian', 'retur_half']: @@ -539,10 +553,28 @@ class RefundSaleOrder(models.Model): if any(field in vals for field in ['uang_masuk', 'invoice_ids', 'ongkir', 'sale_order_ids', 'amount_refund']): total_invoice = sum(self.env['account.move'].browse(invoice_ids).mapped('amount_total_signed')) vals['total_invoice'] = total_invoice - uang_masuk = rec.uang_masuk - can_refund = uang_masuk - total_invoice - + uang_masuk = vals.get('uang_masuk', rec.uang_masuk) amount_refund = vals.get('amount_refund', rec.amount_refund) + can_refund = 0.0 + total_refunded = 0.0 + + if refund_type == 'berita_acara': + can_refund = uang_masuk + remaining = uang_masuk - amount_refund + else: + can_refund = uang_masuk - total_invoice + + existing_refunds = self.search([ + ('sale_order_ids', 'in', so_ids), + ('id', '!=', rec.id) + ]) + total_refunded = sum(existing_refunds.mapped('amount_refund')) + + if existing_refunds: + remaining = uang_masuk - total_refunded + else: + remaining = uang_masuk - amount_refund + if amount_refund > can_refund: raise ValidationError( @@ -592,7 +624,7 @@ class RefundSaleOrder(models.Model): for rec in self: move_links = [] - invoice_ids = rec.sale_order_ids.mapped('invoice_ids') + invoice_ids = rec.sale_order_ids.mapped('invoice_ids').filtered(lambda m: m.state == 'posted') moves = self.env['account.move'].search([ ('sale_id', 'in', rec.sale_order_ids.ids), @@ -602,6 +634,7 @@ class RefundSaleOrder(models.Model): piutangbca = self.env['account.move'] piutangmdr = self.env['account.move'] + cabinvoice = self.env['account.move'] for inv_name in invoice_ids.mapped('name'): piutangbca |= self.env['account.move'].search([ @@ -614,6 +647,11 @@ class RefundSaleOrder(models.Model): ('journal_id', '=', 7), ('state', '=', 'posted'), ]) + cabinvoice |= self.env['account.move'].search([ + ('ref', 'ilike', inv_name), + ('journal_id', '=', 11), + ('state', '=', 'posted'), + ]) moves2 = self.env['account.move'] if rec.sale_order_ids: @@ -659,15 +697,13 @@ class RefundSaleOrder(models.Model): ('line_ids.account_id', '=', 450), ('line_ids.account_id', '=', 668), ] - - if so_names: domain += ['|'] * (len(so_names) - 1) for name in so_names: domain.append(('ref', 'ilike', name)) - moves_ongkir = self.env['account.move'].search(domain) + moves_ongkir = self.env['account.move'].search(domain) - all_moves = moves | piutangbca | piutangmdr | misc | moves2 | moves_ongkir + all_moves = moves | piutangbca | piutangmdr | misc | moves2 | moves_ongkir | cabinvoice for move in all_moves: url = f"/web#id={move.id}&model=account.move&view_type=form" @@ -695,7 +731,7 @@ class RefundSaleOrder(models.Model): for so in self.sale_order_ids: self.ongkir += so.delivery_amt or 0.0 valid_invoices = so.invoice_ids.filtered( - lambda inv: inv.move_type in ['out_invoice', 'out_refund'] and inv.state != 'cancel' + lambda inv: inv.move_type in ['out_invoice', 'out_refund'] and inv.state == 'posted' ) all_invoices |= valid_invoices total_invoice += sum(valid_invoices.mapped('amount_total_signed')) @@ -713,6 +749,7 @@ class RefundSaleOrder(models.Model): piutangbca = self.env['account.move'] piutangmdr = self.env['account.move'] + cabinvoice = self.env['account.move'] for inv_name in all_invoices.mapped('name'): piutangbca |= self.env['account.move'].search([ @@ -725,6 +762,11 @@ class RefundSaleOrder(models.Model): ('journal_id', '=', 7), ('state', '=', 'posted'), ]) + cabinvoice |= self.env['account.move'].search([ + ('ref', 'ilike', inv_name), + ('journal_id', '=', 11), + ('state', '=', 'posted'), + ]) misc = self.env['account.move'] if all_invoices: @@ -799,6 +841,7 @@ class RefundSaleOrder(models.Model): has_moves3 = bool(moves3) has_piutangmdr = bool(piutangmdr) has_piutangbca = bool(piutangbca) + has_cabinvoice = bool(cabinvoice) has_misc = bool(misc) has_ongkir = bool(moves_ongkir) ssos = self.env['sale.order'].browse(so_ids) @@ -812,6 +855,8 @@ class RefundSaleOrder(models.Model): sisa_uang_masuk += sum(moves.mapped('amount_total_signed')) if has_moves2: sisa_uang_masuk += sum(moves2.mapped('amount_total_signed')) + if has_cabinvoice: + sisa_uang_masuk += sum(cabinvoice.mapped('amount_total_signed')) if has_moves3: sisa_uang_masuk += sum(moves3.mapped('amount_total_signed')) if has_piutangbca: diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 17691798..90cd5fa2 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -647,7 +647,7 @@ class SaleOrder(models.Model): def _get_biteship_courier_codes(self): return [ - 'gojek','grab','deliveree','lalamove','jne','tiki','ninja','lion','rara','sicepat','jnt','pos','idexpress','rpx','wahana','jdl','pos','anteraja','sap','paxel','borzo' + 'gojek','grab','deliveree','lalamove','jne','ninja','lion','rara','sicepat','jnt','pos','idexpress','rpx','wahana','jdl','anteraja','sap','paxel','borzo' ] @api.onchange('carrier_id') @@ -2305,7 +2305,7 @@ class SaleOrder(models.Model): search_bom = self.env['mrp.production'].search([('product_id', '=', line.product_id.id), ('sale_order', '=', order.id), ('state', '!=', 'cancel')], order='name desc') if search_bom: - confirmed_bom = search_bom.filtered(lambda x: x.state == 'confirmed' or x.state == 'done') + confirmed_bom = search_bom.filtered(lambda x: x.state in ['confirmed', 'to_close', 'progress', 'done']) if not confirmed_bom: raise UserError( "Product BOM belum dikonfirmasi di Manufacturing Orders. Silakan hubungi Purchasing.") @@ -2316,7 +2316,7 @@ class SaleOrder(models.Model): for order in self: for line in order.order_line: search_product = self.env['sale.order.line'].search( - [('product_id', '=', line.product_id.id), ('order_id', '=', order.id)]) + [('product_id', '=', line.product_id.id), ('order_id', '=', order.id), ('display_type', '=', False)]) if len(search_product) > 1: raise UserError("Terdapat DUPLIKASI data pada Product {}".format(line.product_id.display_name)) @@ -2598,6 +2598,18 @@ class SaleOrder(models.Model): else: return False + def check_archived_product(self): + for order in self: + for line in order.order_line: + if line.product_id.active == False: + raise UserError("Terdapat Product yang sudah di Archive pada Product: {}".format(line.product_id.display_name)) + + def check_archived_uom(self): + for order in self: + for line in order.order_line: + if line.product_uom.active == False: + raise UserError("Terdapat UoM yang sudah di Archive pada UoM {} di Product {}".format(line.product_uom.name, line.product_id.display_name)) + def action_confirm(self): for order in self: order._validate_delivery_amt() @@ -2614,6 +2626,8 @@ class SaleOrder(models.Model): order._validate_order() order._validate_npwp() order.order_line.validate_line() + order.check_archived_product() + order.check_archived_uom() main_parent = order.partner_id.get_main_parent() SYSTEM_UID = 25 @@ -3438,7 +3452,7 @@ class SaleOrder(models.Model): def button_refund(self): self.ensure_one() - invoice_ids = self.invoice_ids.filtered(lambda inv: inv.state != 'cancel') + invoice_ids = self.invoice_ids.filtered(lambda inv: inv.state == 'posted') moves = self.env['account.move'].search([ ('sale_id', '=', self.id), @@ -3447,6 +3461,7 @@ class SaleOrder(models.Model): ]) piutangbca = self.env['account.move'] piutangmdr = self.env['account.move'] + cabinvoice = self.env['account.move'] for inv_name in invoice_ids.mapped('name'): piutangbca |= self.env['account.move'].search([ @@ -3459,6 +3474,11 @@ class SaleOrder(models.Model): ('journal_id', '=', 7), ('state', '=', 'posted'), ]) + cabinvoice |= self.env['account.move'].search([ + ('ref', 'ilike', inv_name), + ('journal_id', '=', 11), + ('state', '=', 'posted'), + ]) moves2 = self.env['account.move'].search([ ('ref', 'ilike', self.name), @@ -3473,6 +3493,7 @@ class SaleOrder(models.Model): has_moves2 = bool(moves2) has_piutangmdr = bool(piutangmdr) has_piutangbca = bool(piutangbca) + has_cabinvoice = bool(cabinvoice) has_settlement = self.payment_status == 'settlement' if has_moves and has_settlement: @@ -3483,6 +3504,8 @@ class SaleOrder(models.Model): total_uang_muka = sum(moves2.mapped('amount_total_signed')) elif has_settlement: total_uang_muka = self.gross_amount + elif has_cabinvoice: + total_uang_muka = sum(cabinvoice.mapped('amount_total_signed')) elif has_piutangbca: total_uang_muka = sum(piutangbca.mapped('amount_total_signed')) elif has_piutangmdr: diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py index 1df1a058..dd44f84a 100644 --- a/indoteknik_custom/models/sale_order_line.py +++ b/indoteknik_custom/models/sale_order_line.py @@ -17,6 +17,7 @@ class SaleOrderLine(models.Model): help="Total % Margin in Sales Order Header") item_percent_margin_before = fields.Float('%Margin Before', compute='_compute_item_percent_margin_before', help="Total % Margin excluding third party in Sales Order Header") + amount_cashback = fields.Float('Cashback Brand', compute='_compute_cashback_brand', help='Cashback from product who has cashback percent in manufacture') initial_discount = fields.Float('Initial Discount') vendor_id = fields.Many2one( 'res.partner', string='Vendor', readonly=True, @@ -164,7 +165,10 @@ class SaleOrderLine(models.Model): purchase_price = line.purchase_price if line.purchase_tax_id.price_include: - purchase_price = line.purchase_price / 1.11 + purchase_price = line.purchase_price / (1 + (line.purchase_tax_id.amount / 100)) + + if line.amount_cashback > 0: + purchase_price = purchase_price - line.amount_cashback purchase_price = purchase_price * line.product_uom_qty margin_per_item = sales_price - purchase_price @@ -186,7 +190,10 @@ class SaleOrderLine(models.Model): purchase_price = line.purchase_price if line.purchase_tax_id and line.purchase_tax_id.price_include: - purchase_price = line.purchase_price / 1.11 + purchase_price = line.purchase_price / (1 + (line.purchase_tax_id.amount / 100)) + + if line.amount_cashback > 0: + purchase_price = purchase_price - line.amount_cashback purchase_price = purchase_price * line.product_uom_qty @@ -215,7 +222,10 @@ class SaleOrderLine(models.Model): purchase_price = line.purchase_price if line.purchase_tax_id.price_include: - purchase_price = line.purchase_price / 1.11 + purchase_price = line.purchase_price / (1 + (line.purchase_tax_id.amount / 100)) + + if line.amount_cashback > 0: + purchase_price = purchase_price - line.amount_cashback purchase_price = purchase_price * line.product_uom_qty margin_per_item = sales_price - purchase_price @@ -241,12 +251,47 @@ class SaleOrderLine(models.Model): purchase_price = line.purchase_price if line.purchase_tax_id.price_include: - purchase_price = line.purchase_price / 1.11 + purchase_price = line.purchase_price / (1 + (line.purchase_tax_id.amount / 100)) + + if line.amount_cashback > 0: + purchase_price = purchase_price - line.amount_cashback purchase_price = purchase_price * line.product_uom_qty margin_per_item = sales_price - purchase_price line.item_before_margin = margin_per_item + def _compute_cashback_brand(self): + start_date = datetime(2026, 2, 1, 0, 0, 0) + for line in self: + line.amount_cashback = 0 + + if not line.product_id: + continue + + if line.order_id.date_order < start_date: + continue + + price, taxes, vendor_id = self._get_purchase_price(line.product_id) + + cashback_percent = line.product_id.x_manufacture.cashback_percent or 0 + if cashback_percent <= 0: + continue + + + if line.vendor_id.id != 5571: + continue + + price_tax_excl = price + + if taxes: + tax = self.env['account.tax'].browse(taxes) + if tax.price_include: + price_tax_excl = price / (1 + (tax.amount / 100)) + else: + price_tax_excl = price + + line.amount_cashback = price_tax_excl * cashback_percent + # @api.onchange('vendor_id') # def onchange_vendor_id(self): # # TODO : need to change this logic @stephan diff --git a/indoteknik_custom/models/shipment_group.py b/indoteknik_custom/models/shipment_group.py index 7203b566..5f5f2d79 100644 --- a/indoteknik_custom/models/shipment_group.py +++ b/indoteknik_custom/models/shipment_group.py @@ -2,6 +2,7 @@ from odoo import models, api, fields from odoo.exceptions import AccessError, UserError, ValidationError from datetime import timedelta, date import logging +from markupsafe import escape as html_escape _logger = logging.getLogger(__name__) @@ -16,7 +17,21 @@ class ShipmentGroup(models.Model): partner_id = fields.Many2one('res.partner', string='Customer') carrier_id = fields.Many2one('delivery.carrier', string='Ekspedisi') total_colly_line = fields.Float(string='Total Colly', compute='_compute_total_colly_line') + is_multi_partner = fields.Boolean(string='Is Multi Partner', compute='_compute_is_multi_partner') + partner_ids = fields.Many2many('res.partner', string='Customers', compute='_compute_partner_ids') + driver = fields.Text(string='Driver') + @api.depends('shipment_line.partner_id') + def _compute_partner_ids(self): + for rec in self: + rec.partner_ids = rec.shipment_line.mapped('partner_id').ids + + @api.depends('shipment_line.partner_id') + def _compute_is_multi_partner(self): + for rec in self: + partners = rec.shipment_line.mapped('partner_id') + rec.is_multi_partner = len(partners) > 1 + def sync_api_shipping(self): for rec in self.shipment_line: picking_names = [lines.picking_id.name for lines in self.shipment_line] @@ -97,14 +112,14 @@ class ShipmentGroupLine(models.Model): @api.onchange('picking_id') def onchange_picking_id(self): if self.picking_id: - picking = self.env['stock.picking'].browse(self.picking_id.id) + picking = self.picking_id if self.shipment_id.carrier_id and self.shipment_id.carrier_id != picking.carrier_id: raise UserError('carrier must be same as shipment group') - + if picking.total_mapping_koli == 0: raise UserError(f'Picking {picking.name} tidak memiliki mapping koli') - + self.partner_id = picking.partner_id self.shipping_paid_by = picking.sale_id.shipping_paid_by self.carrier_id = picking.carrier_id.id @@ -115,6 +130,9 @@ class ShipmentGroupLine(models.Model): self.sale_id = picking.sale_id + if self.shipment_id: + self.shipment_id._compute_is_multi_partner() + @api.model def create(self, vals): record = super(ShipmentGroupLine, self).create(vals) diff --git a/indoteknik_custom/models/solr/apache_solr.py b/indoteknik_custom/models/solr/apache_solr.py index c2283421..21fb15d2 100644 --- a/indoteknik_custom/models/solr/apache_solr.py +++ b/indoteknik_custom/models/solr/apache_solr.py @@ -7,12 +7,12 @@ import time from odoo.tools.config import config _logger = logging.getLogger(__name__) -# _solr = pysolr.Solr('http://10.148.0.5:8983/solr/product/', always_commit=True, timeout=30) -# _variants_solr = pysolr.Solr('http://10.148.0.5:8983/solr/variants/', always_commit=True, timeout=30) -# _recommendation_solr = pysolr.Solr('http://10.148.0.5:8983/solr/recommendation/', always_commit=True, timeout=30) -_solr = pysolr.Solr('http://localhost:8983/solr/product/', always_commit=True, timeout=30) -_variants_solr = pysolr.Solr('http://localhost:8983/solr/variants/', always_commit=True, timeout=30) -_recommendation_solr = pysolr.Solr('http://localhost:8983/solr/recommendation/', always_commit=True, timeout=30) +_solr = pysolr.Solr('http://10.148.0.5:8983/solr/product/', always_commit=True, timeout=30) +_variants_solr = pysolr.Solr('http://10.148.0.5:8983/solr/variants/', always_commit=True, timeout=30) +_recommendation_solr = pysolr.Solr('http://10.148.0.5:8983/solr/recommendation/', always_commit=True, timeout=30) +# _solr = pysolr.Solr('http://35.219.88.139/solr/product/', always_commit=True, timeout=30) +# _variants_solr = pysolr.Solr('http://35.219.88.139/solr/variants/', always_commit=True, timeout=30) +# _recommendation_solr = pysolr.Solr('http://35.219.88.139/solr/recommendation/', always_commit=True, timeout=30) # _solr = pysolr.Solr('http://34.101.189.218:8983/solr/product/', always_commit=True, timeout=30) # for development only @@ -27,7 +27,7 @@ class ApacheSolr(models.Model): if env == 'development': url = 'http://localhost:8983/solr/' elif env == 'production': - url = 'http://locahost:8983/solr/' + url = 'http://34.101.189.218:8983/solr/' return pysolr.Solr(url + schema, always_commit=False, timeout=10) @@ -258,6 +258,7 @@ class ApacheSolr(models.Model): 'id': variant.id, 'display_name_s': variant.display_name, 'name_s': variant.name, + 'search_keywords_t': variant.display_name, 'default_code_s': variant.default_code or '', 'product_rating_f': variant.product_tmpl_id.virtual_rating, 'product_id_i': variant.id, @@ -269,6 +270,7 @@ class ApacheSolr(models.Model): 'tax_f': tax, 'stock_total_f': variant.qty_stock_vendor, 'weight_f': variant.product_tmpl_id.weight, + 'has_magento_b': variant.has_magento, 'attribute_set_id_i': variant.product_tmpl_id.x_attribute_set_id or 0, 'attribute_set_name_s': variant.product_tmpl_id.x_attribute_set_name or '', 'manufacture_id_i': variant.product_tmpl_id.x_manufacture.id or 0, diff --git a/indoteknik_custom/models/solr/product_product.py b/indoteknik_custom/models/solr/product_product.py index 7df56e57..a90bcb98 100644 --- a/indoteknik_custom/models/solr/product_product.py +++ b/indoteknik_custom/models/solr/product_product.py @@ -64,6 +64,7 @@ class ProductProduct(models.Model): 'id': variant.id, 'display_name_s': variant.display_name, 'name_s': variant.name, + 'search_keywords_t': variant.display_name, 'default_code_s': variant.default_code or '', 'product_rating_f': variant.product_tmpl_id.virtual_rating, 'product_id_i': variant.id, @@ -73,6 +74,7 @@ class ProductProduct(models.Model): 'image_mobile_s': ir_attachment.api_image('product.template', 'image_256', variant.product_tmpl_id.id), 'stock_total_f': variant.qty_free_bandengan, 'weight_f': variant.weight, + 'has_magento_b': variant.has_magento, 'attribute_set_id_i': variant.product_tmpl_id.x_attribute_set_id or 0, 'attribute_set_name_s': variant.product_tmpl_id.x_attribute_set_name or '', 'manufacture_id_i': variant.product_tmpl_id.x_manufacture.id or 0, diff --git a/indoteknik_custom/models/stock_inventory.py b/indoteknik_custom/models/stock_inventory.py index 84eb5a17..efd52e5c 100644 --- a/indoteknik_custom/models/stock_inventory.py +++ b/indoteknik_custom/models/stock_inventory.py @@ -17,6 +17,35 @@ class StockInventory(models.Model): ('out', 'Adjusment Out'), ], string='Adjusments Type', required=True) + approval_state = fields.Selection([ + ('logistic', 'Logistic'), + ('accounting', 'Accounting'), + ('approved', 'Approved'), + ], tracking=True, readonly=True) + + def action_validate(self): + if self.adjusment_type == 'out': + + if self.approval_state != 'approved': + + if self.approval_state == 'logistic': + if not self.env.user.has_group('indoteknik_custom.group_role_logistic'): + raise UserError("Adjustment Out harus dilakukan oleh Logistic") + self.approval_state = 'accounting' + return True + + elif self.approval_state == 'accounting': + if not self.env.user.has_group('indoteknik_custom.group_role_fat'): + raise UserError("Adjustment Out harus dilakukan oleh Accounting") + self.approval_state = 'approved' + return super(StockInventory, self).action_validate() + + else: + raise UserError("Adjustment Out harus melalui approval terlebih dahulu.") + + return super(StockInventory, self).action_validate() + + def _generate_number_stock_inventory(self): """Men-generate nomor untuk semua stock inventory yang belum memiliki number.""" stock_records = self.env['stock.inventory'].search([('number', '=', False)], order='id asc') @@ -53,13 +82,19 @@ class StockInventory(models.Model): return "00001" # Jika belum ada data, mulai dari 00001 def action_start(self): - if self.env.user.id not in [21, 17, 571, 28]: - raise UserError("Hanya Rafly, Denise, Iqmal, dan Stephan yang bisa start inventory") + if self.env.user.id not in [21, 17, 571, 28, 25]: + raise UserError("Hanya Rafly, Denise, Iqmal, dan Stephan yang bisa start inventory") return super(StockInventory, self).action_start() @api.model def create(self, vals): """Pastikan nomor hanya dibuat saat penyimpanan.""" + + if vals.get('adjusment_type') == 'in': + vals['approval_state'] = False + elif vals.get('adjusment_type') == 'out': + vals['approval_state'] = 'logistic' + if 'adjusment_type' in vals and not vals.get('number'): vals['number'] = False # Jangan buat number otomatis dulu @@ -69,12 +104,17 @@ class StockInventory(models.Model): self._assign_number(order) # Generate number setelah save return order + def write(self, vals): """Jika adjusment_type diubah, generate ulang nomor.""" res = super(StockInventory, self).write(vals) if 'adjusment_type' in vals: for record in self: + if record.adjusment_type == 'in': + record.approval_state = False + elif record.adjusment_type == 'out' and record.approval_state == False: + record.approval_state = 'logistic' self._assign_number(record) return res diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 2465fa96..065b1484 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -203,6 +203,16 @@ class StockPicking(models.Model): is_so_fiktif = fields.Boolean('SO Fiktif?', compute='_compute_is_so_fiktif', tracking=3) payment_term = fields.Char('Payment Term', compute='_get_partner_payment_term') is_rev_tg = fields.Boolean('Administrasi') + shipment_group_id = fields.Many2one('shipment.group', string='Shipment Group', compute='_compute_shipment_group_id') + + @api.depends('shipment_group_id') + def _compute_shipment_group_id(self): + for record in self: + shipment_line = self.env['shipment.group.line'].search([('picking_id', '=', record.id)], limit=1) + if shipment_line: + record.shipment_group_id = shipment_line.shipment_id.id + else: + record.shipment_group_id = False @api.depends('sale_id.payment_term_id') def _get_partner_payment_term(self): @@ -1387,7 +1397,8 @@ class StockPicking(models.Model): ]) if quant: - return quant.quantity + return sum(quant.mapped('quantity')) + # return quant.quantity return 0 diff --git a/indoteknik_custom/models/tukar_guling_po.py b/indoteknik_custom/models/tukar_guling_po.py index ae58d509..1ee10679 100644 --- a/indoteknik_custom/models/tukar_guling_po.py +++ b/indoteknik_custom/models/tukar_guling_po.py @@ -582,7 +582,23 @@ class TukarGulingPO(models.Model): ('group_id', '=', group.id), ('state', '=', 'done') ]) - bu_inputs = po_pickings.filtered(lambda p: p.picking_type_id.id == 28) + + product_ids = set(record.line_ids.mapped("product_id").ids) + + _logger.info("TG product_ids: %s", product_ids) + + def _get_moves(picking): + return picking.move_ids_without_package if picking.move_ids_without_package else picking.move_lines + + bu_inputs = po_pickings.filtered( + lambda p: p.picking_type_id.id == 28 and any( + m.product_id.id in product_ids + for m in _get_moves(p) + ) + ) + + _logger.info("BU INPUT dengan product sama: %s", bu_inputs.mapped("name")) + bu_puts = po_pickings.filtered(lambda p: p.picking_type_id.id == 75) else: raise UserError("Group ID tidak ditemukan pada BU Operations.") @@ -711,12 +727,26 @@ class TukarGulingPO(models.Model): # Ambil pasangannya di BU INPUT (asumsi urutan sejajar) sorted_bu_puts = sorted(bu_puts, key=lambda p: p.name) + # sorted_bu_inputs = sorted(bu_inputs, key=lambda p: p.name) + + # if bu_put_index >= len(sorted_bu_inputs): + # raise UserError("Tidak ditemukan pasangan BU INPUT untuk BU PUT yang dipilih.") + + # paired = [(sorted_bu_puts[bu_put_index], sorted_bu_inputs[bu_put_index])] sorted_bu_inputs = sorted(bu_inputs, key=lambda p: p.name) - if bu_put_index >= len(sorted_bu_inputs): - raise UserError("Tidak ditemukan pasangan BU INPUT untuk BU PUT yang dipilih.") + if not sorted_bu_inputs: + raise UserError( + "Tidak ditemukan BU INPUT yang memiliki product TG." + ) - paired = [(sorted_bu_puts[bu_put_index], sorted_bu_inputs[bu_put_index])] + paired = [(record.operations, sorted_bu_inputs[0])] + + _logger.info( + "🔗 Pairing BU PUT %s dengan BU INPUT %s", + record.operations.name, + sorted_bu_inputs[0].name + ) for bu_put, bu_input in paired: vrt = _create_return_from_picking(bu_put, bu_put_qty_map) diff --git a/indoteknik_custom/models/update_depreciation_move_wizard.py b/indoteknik_custom/models/update_depreciation_move_wizard.py new file mode 100644 index 00000000..7d465f1d --- /dev/null +++ b/indoteknik_custom/models/update_depreciation_move_wizard.py @@ -0,0 +1,48 @@ +from odoo import models, fields, api +from odoo.exceptions import UserError + +class UpdateDepreciationMoveWizard(models.TransientModel): + _name = 'update.depreciation.move.wizard' + _description = 'Wizard untuk Update Move Check Depreciation Line' + + target_date = fields.Date(string="Tanggal Depresiasi", required=True) + + # def action_update_move_check(self): + # lines = self.env['account.asset.depreciation.line'].search([ + # ('depreciation_date', '=', self.target_date), + # ]) + # if not lines: + # raise UserError("Tidak ada baris depresiasi dengan tanggal tersebut.") + + # updated_count = 0 + # for line in lines: + # if not line.move_check: + # line.move_check = True + # line.move_posted_check = True + # updated_count += 1 + + # return { + # 'type': 'ir.actions.client', + # 'tag': 'display_notification', + # 'params': { + # 'title': 'Update Selesai', + # 'message': f'{updated_count} baris berhasil di-update.', + # 'type': 'success', + # 'sticky': False, + # } + # } + + def action_update_move_check(self): + assets = self.env['account.asset.asset'] + assets.compute_generated_entries(self.target_date) + + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'title': 'Update Selesai', + 'message': 'Depresiasi berhasil di-update.', + 'type': 'success', + 'sticky': False, + } + }
\ No newline at end of file diff --git a/indoteknik_custom/models/x_manufactures.py b/indoteknik_custom/models/x_manufactures.py index b4b61296..0c3bfa3b 100755 --- a/indoteknik_custom/models/x_manufactures.py +++ b/indoteknik_custom/models/x_manufactures.py @@ -50,6 +50,7 @@ class XManufactures(models.Model): # user_id = fields.Many2one('res.users', string='Responsible', domain="['|'('id', '=', 19), ('id', '=', 6)]", help="Siapa yang bertanggung jawab") user_id = fields.Many2one('res.users', string='Responsible', help="Siapa yang bertanggung jawab") override_vendor_id = fields.Many2one('res.partner', string='Override Vendor') + cashback_percent = fields.Float(string='Cashback Percent', default=0) def _compute_vendor_ids(self): for manufacture in self: diff --git a/indoteknik_custom/report/purchase_report.xml b/indoteknik_custom/report/purchase_report.xml index 54ac6193..81129dde 100644 --- a/indoteknik_custom/report/purchase_report.xml +++ b/indoteknik_custom/report/purchase_report.xml @@ -67,7 +67,7 @@ ADM. JAKARTA UTARA, DKI JAKARTA </t> <t t-else=""> - Jl. Bandengan Utara Komp A 8 B RT.<br/> + Jl. Bandengan Utara Komp A & B RT.<br/> Penjaringan, Kec. Penjaringan, Jakarta<br/> (BELAKANG INDOMARET) </t> @@ -106,7 +106,7 @@ <div style="display:flex; flex-direction:column; flex:1;"> <span style="font-weight:bold; margin-bottom:2px;"> <t t-esc="line_index + 1"/>. - <t t-if="line.product_id.id in [114360, 595346, 610166]"> + <t t-if="line.product_id.id in [114360, 595346, 610166, 420315]"> <t t-esc="line.name"/> </t> <t t-else=""> diff --git a/indoteknik_custom/report/purchase_report_internal.xml b/indoteknik_custom/report/purchase_report_internal.xml new file mode 100644 index 00000000..7df847de --- /dev/null +++ b/indoteknik_custom/report/purchase_report_internal.xml @@ -0,0 +1,201 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <data> + <!-- Report Action --> + <record id="action_report_purchaseorder_internal" model="ir.actions.report"> + <field name="name">Purchase Order (Internal)</field> + <field name="model">purchase.order</field> + <field name="report_type">qweb-pdf</field> + <field name="report_name">indoteknik_custom.report_purchaseorder_internal</field> + <field name="report_file">indoteknik_custom.report_purchaseorder_internal</field> + <field name="print_report_name"> + ('%s - %s' % (object.name, object.partner_id.name)) + </field> + <field name="binding_model_id" ref="purchase.model_purchase_order"/> + <field name="binding_type">report</field> + </record> + </data> + + <!-- Wrapper Template --> + <template id="report_purchaseorder_internal"> + <t t-call="web.html_container"> + <t t-foreach="docs" t-as="doc"> + <t t-call="indoteknik_custom.report_purchaseorder_internal_document" t-lang="doc.partner_id.lang"/> + </t> + </t> + </template> + + <template id="report_purchaseorder_internal_document"> + <t t-call="web.html_container"> + <t t-set="doc" t-value="doc.with_context(lang=doc.partner_id.lang)" /> + + <!-- HEADER --> + <div class="header"> + <img src="https://erp.indoteknik.com/api/image/ir.attachment/datas/2498521" + style="width:100%; display:block;"/> + </div> + + <!-- PAGE CONTENT --> + <div class="article" style="margin: 0 1.5cm 0 1.5cm; font-family:Arial, sans-serif; font-size:14px; color:#333;"> + + <!-- TITLE --> + <h2 style="text-align:center; margin:8px 0 0 0; color:#d32f2f; font-weight:800; letter-spacing:1px;"> + PURCHASE ORDER + </h2> + <h4 style="text-align:center; margin:4px 0 20px 0; font-weight:normal; color:#555;"> + No. <span t-field="doc.name"/> + </h4> + + <!-- TOP INFO --> + <table style="width:100%; margin-bottom:20px; border-radius:8px; box-shadow:0 1px 4px rgba(0,0,0,0.1); overflow:hidden; border:1px solid #ddd;"> + <tr style="background:#fafafa;"> + <td style="padding:10px 12px;"><strong>Term Of Payment:</strong> <span t-field="doc.payment_term_id.name"/></td> + <td style="padding:10px 12px;"><strong>Order Date:</strong> <span t-field="doc.date_order" t-options='{"widget": "date"}'/></td> + <td style="padding:10px 12px;"><strong>Responsible:</strong> <span t-field="doc.user_id"/></td> + </tr> + </table> + + <!-- VENDOR & DELIVERY --> + <table style="width:100%; margin-bottom:24px; border-spacing:16px 0;"> + <tr> + <td style="width:50%; border:1px solid #ccc; border-radius:8px; padding:10px; background:#fcfcfc; vertical-align:top;"> + <strong style="color:#d32f2f;">Alamat Pengiriman</strong><br/> + PT. Indoteknik Dotcom Gemilang<br/> + <t t-if="doc.overseas_po == True"> + JALAN BANDENGAN UTARA 85A NO.8-9 RT. 003<br/> + RW. 016, PENJARINGAN, PENJARINGAN, KOTA<br/> + ADM. JAKARTA UTARA, DKI JAKARTA + </t> + <t t-else=""> + Jl. Bandengan Utara Komp A & B RT.<br/> + Penjaringan, Kec. Penjaringan, Jakarta<br/> + (BELAKANG INDOMARET) + </t> + <br/> + Daerah Khusus Ibukota Jakarta 14440 + </td> + <td style="width:50%; border:1px solid #ccc; border-radius:8px; padding:10px; background:#fcfcfc; vertical-align:top;"> + <strong style="color:#d32f2f;">Nama Vendor</strong><br/> + <span t-field="doc.partner_id.name"/><br/> + <span t-field="doc.partner_id.street"/><br/> + <span t-field="doc.partner_id.city"/> - <span t-field="doc.partner_id.zip"/> + </td> + </tr> + </table> + + <!-- ORDER LINES --> + <table style="border-collapse:collapse; width:100%; margin-top:16px; font-size:14px;"> + <tbody> + <!-- HEADER --> + <tr style="background:#e53935; color:white;"> + <th style="border:1px solid #ccc; padding:8px; text-align:left;">No. & Description</th> + <th style="border:1px solid #ccc; padding:8px; text-align:left;">Image</th> + <th style="border:1px solid #ccc; padding:8px; text-align:center;">Quantity</th> + <th style="border:1px solid #ccc; padding:8px; text-align:center;">Unit Price</th> + <th style="border:1px solid #ccc; padding:8px; text-align:center;">Taxes</th> + <th style="border:1px solid #ccc; padding:8px; text-align:center;">Subtotal</th> + </tr> + + <!-- ISI ORDER LINE --> + <t t-foreach="doc.order_line" t-as="line" t-index="line_index"> + <tr t-attf-style="background-color: #{ '#fafafa' if line_index % 2 == 0 else 'white' };"> + + <!-- NO & DESCRIPTION + IMAGE --> + <td style="border:1px solid #ccc; padding: 6px; display:flex; align-items:center; gap:10px;"> + <!-- TEKS --> + <div style="display:flex; flex-direction:column; flex:1;"> + <span style="font-weight:bold; margin-bottom:2px;"> + <t t-esc="line_index + 1"/>. + <t t-if="line.product_id.id in [114360, 595346, 610166, 420315]"> + <t t-esc="line.name"/> + </t> + <t t-else=""> + <t t-esc="line.product_id.display_name"/> + </t> + </span> + </div> + + </td> + + <td style="border:1px solid #ccc; padding:6px; text-align:center;"> + <t t-if="line.image_small"> + <img t-att-src="image_data_uri(line.image_small)" + style="width:100px; height:100px; object-fit:contain; border:1px solid #ddd; border-radius:6px; background:#fff;"/> + </t> + </td> + <!-- QTY --> + <td style="border:1px solid #ccc; padding:6px; text-align:center;"> + <span t-field="line.product_qty"/> <span t-field="line.product_uom"/> + </td> + + <!-- UNIT PRICE --> + <td style="border:1px solid #ccc; padding:6px; text-align:center;"> + <span t-field="line.price_unit"/> + </td> + + <!-- TAXES --> + <td style="border:1px solid #ccc; padding:6px; text-align:center;"> + <span t-esc="', '.join(map(lambda x: (x.description or x.name), line.taxes_id))"/> + </td> + + <!-- SUBTOTAL --> + <td style="border:1px solid #ccc; padding:6px; text-align:right; font-weight:bold;"> + <span t-field="line.price_subtotal"/> + </td> + </tr> + + <!-- WEBSITE DESCRIPTION --> + <t t-if="line.show_description == True"> + <tr t-attf-style="background-color: #{ '#fef5f5' if line_index % 2 == 0 else '#fffafa' }; "> + <td colspan="6" style="padding: 10px 14px; font-size:10px; line-height:1.3; font-style:italic; color:#555; border-left:1px solid #ccc; border-right:1px solid #ccc; border-bottom:1px solid #ccc;"> + <div t-raw="line.product_id.website_description"/> + </td> + </tr> + </t> + </t> + </tbody> + </table> + + + <!-- TOTALS --> + <table style="margin-top:24px; margin-left:auto; width:40%; font-size:14px; border:1px solid #ddd; border-radius:6px; box-shadow:0 1px 3px rgba(0,0,0,0.08);"> + <tr style="background:#fafafa;"> + <td style="padding:8px;"><strong>Subtotal</strong></td> + <td style="text-align:right; padding:8px;"><span t-field="doc.amount_untaxed"/></td> + </tr> + <tr> + <td style="padding:8px;">Taxes</td> + <td style="text-align:right; padding:8px;"><span t-field="doc.amount_tax"/></td> + </tr> + <tr style="background:#fbe9e7; font-weight:bold; color:#d32f2f;"> + <td style="padding:8px;">Total</td> + <td style="text-align:right; padding:8px;"><span t-field="doc.amount_total"/></td> + </tr> + <tr> + <td style="padding:8px;">Margin PO %</td> + <td style="text-align:right; padding:8px;"><span t-field="doc.total_percent_margin"/></td> + </tr> + <tr> + <td style="padding:8px;">Margin SO %</td> + <td style="text-align:right; padding:8px;"><span t-field="doc.total_so_percent_margin"/></td> + </tr> + </table> + + <!-- NOTES --> + <div style="margin-top:24px; padding:12px; border-top:1px solid #ddd; font-style:italic; color:#555;"> + <p t-field="doc.notes"/> + </div> + </div> + + <!-- STATIC FOOTER --> + <div class="footer"> + <img src="https://erp.indoteknik.com/api/image/ir.attachment/datas/2859765" + style="width:100%; display:block;"/> + </div> + + </t> + </template> + + + +</odoo> diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index 796e5275..4ffefc68 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -199,6 +199,7 @@ access_advance_payment_settlement,access.advance.payment.settlement,model_advanc access_advance_payment_usage_line,access.advance.payment.usage.line,model_advance_payment_usage_line,,1,1,1,1 access_advance_payment_create_bill,access.advance.payment.create.bill,model_advance_payment_create_bill,,1,1,1,1 access_create_reimburse_cab_wizard_user,create.reimburse.cab.wizard user,model_create_reimburse_cab_wizard,,1,1,1,1 +access_advance_payment_cancel_wizard,advance.payment.cancel.wizard,model_advance_payment_cancel_wizard,,1,1,1,1 access_purchasing_job_seen,purchasing.job.seen,model_purchasing_job_seen,,1,1,1,1 access_tukar_guling_all_users,tukar.guling.all.users,model_tukar_guling,base.group_user,1,1,1,1 @@ -214,4 +215,5 @@ access_surat_piutang_user,surat.piutang user,model_surat_piutang,,1,1,1,1 access_surat_piutang_line_user,surat.piutang.line user,model_surat_piutang_line,,1,1,1,1 access_sj_tele,access.sj.tele,model_sj_tele,base.group_system,1,1,1,1 access_stock_picking_sj_document,stock.picking.sj.document,model_stock_picking_sj_document,base.group_user,1,1,1,1 +access_update_depreciation_move_wizard,access.update.depreciation.move.wizard,model_update_depreciation_move_wizard,,1,1,1,1 access_keywords,keywords,model_keywords,base.group_user,1,1,1,1 diff --git a/indoteknik_custom/views/account_move_line.xml b/indoteknik_custom/views/account_move_line.xml index cb24a0f0..838596c8 100644 --- a/indoteknik_custom/views/account_move_line.xml +++ b/indoteknik_custom/views/account_move_line.xml @@ -22,4 +22,16 @@ </field> </record> </data> + <data> + <record id="action_gl_reconcile_server" model="ir.actions.server"> + <field name="name">Reconcile Selected</field> + <field name="model_id" ref="account.model_account_move_line"/> + <field name="binding_model_id" ref="account.model_account_move_line"/> + <field name="binding_view_types">list</field> + <field name="state">code</field> + <field name="code"> + action = records.action_gl_reconcile() + </field> + </record> + </data> </odoo> diff --git a/indoteknik_custom/views/advance_payment_request.xml b/indoteknik_custom/views/advance_payment_request.xml index dd6370c7..340e0caf 100644 --- a/indoteknik_custom/views/advance_payment_request.xml +++ b/indoteknik_custom/views/advance_payment_request.xml @@ -4,7 +4,7 @@ <field name="name">advance.payment.request.form</field> <field name="model">advance.payment.request</field> <field name="arch" type="xml"> - <form string="Advance Payment Request & Reimburse" duplicate="0"> + <form string="Advance Payment Request & Reimburse" duplicate="0" delete="false"> <header> <button name="action_realisasi_pum" type="object" @@ -18,7 +18,7 @@ type="object" string="Checking/Approval" class="btn-success" - attrs="{'invisible': [('status', 'in', ['approved','reject','draft'])]}"/> + attrs="{'invisible': [('status', 'in', ['approved','cancel','draft'])]}"/> <button name="action_confirm_payment" type="object" string="Konfirmasi Pembayaran" @@ -44,6 +44,11 @@ ('is_cab_visible', '=', True), ('type_request', '!=', 'reimburse') ]}"/> + <button name="action_open_cancel_wizard" + string="Cancel" + type="object" + class="btn-danger" + attrs="{'invisible': ['|',('status', 'in', ['draft', 'cancel']), ('status_pay_down_payment', '=', 'payment')]}"/> <field name="status" widget="statusbar" statusbar_visible="draft,pengajuan1,pengajuan2,pengajuan3,approved" statusbar_colors='{"reject":"red"}' @@ -51,7 +56,8 @@ </header> <sheet> <widget name="web_ribbon" title="Payment" attrs="{'invisible': ['|', ('status_pay_down_payment', '!=', 'payment'), ('status', '=', 'draft')]}"/> - <widget name="web_ribbon" title="Pending" bg_color="bg-danger" attrs="{'invisible': ['|', ('status_pay_down_payment', '!=', 'pending'), ('status', '=', 'draft')]}"/> + <widget name="web_ribbon" title="Pending" bg_color="bg-warning" attrs="{'invisible': ['|', ('status_pay_down_payment', '!=', 'pending'), ('status', '=', 'draft')]}"/> + <widget name="web_ribbon" title="Cancel" bg_color="bg-danger" attrs="{'invisible': ['|', ('status_pay_down_payment', '!=', 'cancel'), ('status', '=', 'draft')]}"/> <div class="oe_button_box" name="button_box" style="right: 150px;"> <field name="has_settlement" invisible="1"/> <button name="action_realisasi_pum" @@ -108,9 +114,10 @@ <field name="approved_by" readonly="1"/> <field name="create_date" readonly="1"/> <field name="status_pay_down_payment" - readonly="1" + readonly="1" decoration-success="status_pay_down_payment == 'payment'" - decoration-danger="status_pay_down_payment == 'pending'" + decoration-warning="status_pay_down_payment == 'pending'" + decoration-danger="status_pay_down_payment == 'cancel'" widget="badge" invisible = "1"/> </group> <group string="Bukti Transfer"> @@ -194,7 +201,7 @@ <field name="name">advance.payment.request.tree</field> <field name="model">advance.payment.request</field> <field name="arch" type="xml"> - <tree> + <tree delete="false"> <field name="number"/> <field name="applicant_name"/> <field name="nominal"/> @@ -207,7 +214,8 @@ <field name="status_pay_down_payment" readonly="1" decoration-success="status_pay_down_payment == 'payment'" - decoration-danger="status_pay_down_payment == 'pending'" + decoration-warning="status_pay_down_payment == 'pending'" + decoration-danger="status_pay_down_payment == 'cancel'" widget="badge"/> <field name="days_remaining" readonly="1" widget="badge" optional="hide"/> <field name="estimated_return_date" widget="badge" optional="hide"/> @@ -228,6 +236,7 @@ <filter string="PUM" name="filter_pum" domain="[('type_request','=','pum')]"/> <filter string="Reimburse" name="filter_reimburse" domain="[('type_request','=','reimburse')]"/> <separator/> + <filter string="Cancelled" name="filter_cancelled" domain="[('status','=','cancel')]"/> <filter string="Waiting for Approval" name="filter_waiting_approval" domain="[('status','in',['pengajuan1','pengajuan2','pengajuan3'])]"/> <filter string="Approved" name="filter_approved" domain="[('status','=','approved')]"/> <separator/> @@ -316,4 +325,34 @@ <field name="view_id" ref="view_form_create_reimburse_cab_wizard"/> <field name="target">new</field> </record> + + <record id="view_advance_payment_cancel_wizard_form" model="ir.ui.view"> + <field name="name">advance.payment.cancel.wizard.form</field> + <field name="model">advance.payment.cancel.wizard</field> + <field name="arch" type="xml"> + <form string="Batalkan Pengajuan"> + <sheet> + <group> + <div class="alert alert-warning" role="alert" style="margin-bottom: 10px;"> + Apakah Anda yakin ingin membatalkan pengajuan ini? + Silakan isi alasan pembatalan di bawah ini. + </div> + </group> + <group> + <field name="reason"/> + </group> + </sheet> + <footer> + <button name="action_confirm_cancel" + string="Konfirmasi Cancel" + type="object" + class="btn-danger"/> + + <button string="Tutup" + class="btn-secondary" + special="cancel"/> + </footer> + </form> + </field> + </record> </odoo>
\ No newline at end of file diff --git a/indoteknik_custom/views/advance_payment_settlement.xml b/indoteknik_custom/views/advance_payment_settlement.xml index c2b6a26f..a8bf1de7 100644 --- a/indoteknik_custom/views/advance_payment_settlement.xml +++ b/indoteknik_custom/views/advance_payment_settlement.xml @@ -3,7 +3,7 @@ <field name="name">advance.payment.settlement.form</field> <field name="model">advance.payment.settlement</field> <field name="arch" type="xml"> - <form string="Advance Payment Settlement" duplicate="0"> + <form string="Advance Payment Settlement" duplicate="0" delete="false"> <header> <button name="action_cab" type="object" diff --git a/indoteknik_custom/views/approval_date_doc.xml b/indoteknik_custom/views/approval_date_doc.xml index 3d597aa8..a3aae3b4 100644 --- a/indoteknik_custom/views/approval_date_doc.xml +++ b/indoteknik_custom/views/approval_date_doc.xml @@ -14,6 +14,7 @@ <field name="approve_date"/> <field name="approve_by"/> <field name="create_uid"/> + <field name="create_date"/> </tree> </field> </record> @@ -46,6 +47,7 @@ <field name="approve_date"/> <field name="approve_by"/> <field name="create_uid"/> + <field name="create_date"/> <field name="note" attrs="{'invisible': [('state', '!=', 'cancel')]}"/> <field name="state" readonly="1"/> </group> diff --git a/indoteknik_custom/views/price_group.xml b/indoteknik_custom/views/price_group.xml index 5db8756e..7fad8d8d 100644 --- a/indoteknik_custom/views/price_group.xml +++ b/indoteknik_custom/views/price_group.xml @@ -26,6 +26,8 @@ <field name="group6"/> <field name="group7"/> <field name="group8"/> + <field name="group9"/> + <field name="group10"/> </tree> </field> </record> @@ -48,6 +50,8 @@ <field name="group6"/> <field name="group7"/> <field name="group8"/> + <field name="group9"/> + <field name="group10"/> </group> </group> </sheet> diff --git a/indoteknik_custom/views/product_product.xml b/indoteknik_custom/views/product_product.xml index 1d04e708..ee03779f 100644 --- a/indoteknik_custom/views/product_product.xml +++ b/indoteknik_custom/views/product_product.xml @@ -20,6 +20,7 @@ <field name="qty_free_bandengan" optional="hide"/> <field name="qty_rpo" optional="hide"/> <field name="plafon_qty" optional="hide"/> + <!-- <field name="has_magento" optional="hide"/> --> </field> </field> </record> diff --git a/indoteknik_custom/views/product_public_category.xml b/indoteknik_custom/views/product_public_category.xml index a45e0a2e..c2ee154d 100755 --- a/indoteknik_custom/views/product_public_category.xml +++ b/indoteknik_custom/views/product_public_category.xml @@ -22,6 +22,7 @@ <field name="image" widget="image"/> <field name="sequence_frontend"/> <field name="parent_frontend_id"/> + <field name="short_desc"/> </xpath> <xpath expr="//form[1]/sheet[1]/div[1]/group[1]" position="after"> <notebook> diff --git a/indoteknik_custom/views/product_template.xml b/indoteknik_custom/views/product_template.xml index 1933a5d1..9fbf007a 100755 --- a/indoteknik_custom/views/product_template.xml +++ b/indoteknik_custom/views/product_template.xml @@ -78,6 +78,7 @@ <field name="inherit_id" ref="product.product_normal_form_view"/> <field name="arch" type="xml"> <field name="last_update_solr" position="after"> + <field name="has_magento"/> <field name="barcode_box" /> <field name="qty_pcs_box" /> <field name="clean_website_description" /> @@ -114,6 +115,7 @@ <field name="solr_flag"/> <field name="x_attribute_set_id"/> <field name="x_attribute_set_name"/> + <!-- <field name="has_magento"/> --> </field> </field> </record> @@ -127,6 +129,7 @@ <field name="usage"/> <field name="specification"/> <field name="material"/> + <field name="has_magento" /> </field> </field> </record> diff --git a/indoteknik_custom/views/purchase_order.xml b/indoteknik_custom/views/purchase_order.xml index 16b8bd44..59e317d2 100755 --- a/indoteknik_custom/views/purchase_order.xml +++ b/indoteknik_custom/views/purchase_order.xml @@ -105,6 +105,7 @@ <field name="amount_total" position="after"> <field name="total_margin"/> <field name="total_so_margin"/> + <field name="amount_cashback"/> <field name="total_percent_margin"/> <field name="total_so_percent_margin"/> </field> diff --git a/indoteknik_custom/views/purchasing_job.xml b/indoteknik_custom/views/purchasing_job.xml index 6b8baa53..8e1eb3b6 100644 --- a/indoteknik_custom/views/purchasing_job.xml +++ b/indoteknik_custom/views/purchasing_job.xml @@ -16,7 +16,8 @@ <field name="outgoing"/> <field name="status_apo" invisible="1"/> <field name="action"/> - <field name="note"/> + <field name="note" optional="hide"/> + <field name="note_detail"/> <field name="date_po" optional="hide"/> <field name="so_number"/> <field name="check_pj" invisible="1"/> @@ -58,7 +59,8 @@ <field name="outgoing"/> <field name="purchase_representative_id"/> <field name="vendor_id"/> - <field name="note"/> + <!-- <field name="note"/> --> + <field name="note_detail"/> </group> </group> </sheet> diff --git a/indoteknik_custom/views/purchasing_job_state.xml b/indoteknik_custom/views/purchasing_job_state.xml index c7c40020..ca08e44c 100644 --- a/indoteknik_custom/views/purchasing_job_state.xml +++ b/indoteknik_custom/views/purchasing_job_state.xml @@ -8,7 +8,8 @@ <sheet> <group> <field name="status_apo" readonly="1"/> - <field name="note"/> + <field name="note" invisible="1"/> + <field name="note_detail"/> </group> </sheet> </form> diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index 23fbe155..c3df92ec 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -302,6 +302,7 @@ ] } "/> + <field name="amount_cashback"/> <field name="purchase_price_md" optional="hide"/> <field name="purchase_tax_id" attrs="{'readonly': [('parent.approval_status', '!=', False)]}" diff --git a/indoteknik_custom/views/shipment_group.xml b/indoteknik_custom/views/shipment_group.xml index c3f79bda..e348867c 100644 --- a/indoteknik_custom/views/shipment_group.xml +++ b/indoteknik_custom/views/shipment_group.xml @@ -7,6 +7,7 @@ <tree default_order="create_date desc"> <field name="number"/> <field name="partner_id"/> + <field name="partner_ids" widget="many2many_tags" optional="hide"/> <field name="carrier_id"/> <field name="total_colly_line"/> </tree> @@ -42,9 +43,12 @@ <group> <group> <field name="number" readonly="1"/> + <field name="is_multi_partner" readonly="1"/> </group> <group> - <field name="partner_id" readonly="1"/> + <field name="partner_id" readonly="1" attrs="{'invisible': [('is_multi_partner', '=', True)]}"/> + <field name="partner_ids" readonly="1" widget="many2many_tags" attrs="{'invisible': [('is_multi_partner', '=', False)]}"/> + <field name="driver" attrs="{'invisible': [('carrier_id', '!=', 1)]}"/> <field name="carrier_id" readonly="1"/> <field name="total_colly_line" readonly="1"/> </group> diff --git a/indoteknik_custom/views/stock_inventory.xml b/indoteknik_custom/views/stock_inventory.xml index db85f05c..ab1b6eec 100644 --- a/indoteknik_custom/views/stock_inventory.xml +++ b/indoteknik_custom/views/stock_inventory.xml @@ -9,6 +9,7 @@ <xpath expr="//field[@name='location_ids']" position="after"> <field name="number" readonly="1"/> <field name="adjusment_type" /> + <field name="approval_state" /> </xpath> </field> </record> @@ -21,6 +22,8 @@ <field name="arch" type="xml"> <xpath expr="//field[@name='date']" position="after"> <field name="number"/> + <field name="adjusment_type" /> + <field name="approval_state"/> </xpath> </field> </record> diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 9aa0581c..9cd63e25 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -245,6 +245,7 @@ <field name="responsible"/> <field name="carrier_id" attrs="{'invisible': [('select_shipping_option_so', '=', 'biteship')]}"/> + <field name="shipment_group_id"/> <field name="biteship_id" invisible="1"/> <field name="out_code" attrs="{'invisible': [['out_code', '=', False]]}"/> <field name="picking_code" attrs="{'invisible': [['picking_code', '=', False]]}"/> diff --git a/indoteknik_custom/views/tukar_guling.xml b/indoteknik_custom/views/tukar_guling.xml index 8cfb5680..609dea15 100644 --- a/indoteknik_custom/views/tukar_guling.xml +++ b/indoteknik_custom/views/tukar_guling.xml @@ -133,5 +133,29 @@ </form> </field> </record> + <record id="view_tukar_guling_filter" model="ir.ui.view"> + <field name="name">tukar.guling.filter</field> + <field name="model">tukar.guling</field> + <field name="arch" type="xml"> + <search string="Tukar Guling"> + <field name="name" string="No. Dokumen"/> + <field name="partner_id" string="Customer"/> + <field name="origin" string="SO Number"/> + <field name="operations" string="Operations"/> + <!-- <filter string="Pengajuan Saya" name="my_tukar_guling" domain="[('create_uid', '=', uid)]"/> --> + <separator/> + <filter string="Tukar Guling" name="tukar_guling" domain="[('return_type', '=', 'tukar_guling')]"/> + <filter string="Return SO" name="return_so" domain="[('return_type', '=', 'retur_so')]"/> + <separator/> + <filter string="Approval Sales" name="approval_sales" domain="[('state', '=', 'approval_sales')]"/> + <filter string="Approval Logistic" name="approval_logistic" domain="[('state', '=', 'approval_logistic')]"/> + <filter string="Approval Finance" name="approval_finance" domain="[('state', '=', 'approval_finance')]"/> + <filter string="Approved" name="approved" domain="[('state', '=', 'approved')]"/> + <separator/> + <filter string="Done" name="done" domain="[('state', '=', 'done')]"/> + <filter string="Cancelled" name="cancel" domain="[('state', '=', 'cancel')]"/> + </search> + </field> + </record> </data> </odoo> diff --git a/indoteknik_custom/views/update_depreciation_move_wizard_view.xml b/indoteknik_custom/views/update_depreciation_move_wizard_view.xml new file mode 100644 index 00000000..ff128a71 --- /dev/null +++ b/indoteknik_custom/views/update_depreciation_move_wizard_view.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<odoo> + <record id="view_update_depreciation_move_wizard_form" model="ir.ui.view"> + <field name="name">update.depreciation.move.wizard.form</field> + <field name="model">update.depreciation.move.wizard</field> + <field name="arch" type="xml"> + <form string="Update Move Check"> + <group> + <field name="target_date"/> + </group> + <footer> + <button string="Update" type="object" name="action_update_move_check" class="btn-primary"/> + <button string="Batal" special="cancel" class="btn-secondary"/> + </footer> + </form> + </field> + </record> + + <record id="update_depreciation_move_wizard_action" model="ir.actions.act_window"> + <field name="name">Update Depreciation Asset</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">update.depreciation.move.wizard</field> + <field name="view_mode">form</field> + <field name="target">new</field> + </record> + + <menuitem + id="menu_update_depreciation_move_wizard" + name="Update Depreciation Asset" + parent="account.menu_finance_entries_management" + sequence="4" + action="update_depreciation_move_wizard_action" + /> +</odoo> +
\ No newline at end of file diff --git a/indoteknik_custom/views/x_manufactures.xml b/indoteknik_custom/views/x_manufactures.xml index 02061251..b52fe795 100755 --- a/indoteknik_custom/views/x_manufactures.xml +++ b/indoteknik_custom/views/x_manufactures.xml @@ -82,6 +82,7 @@ <field name="x_negara_asal"/> <field name="x_short_desc"/> <field name="x_manufacture_level"/> + <field name="cashback_percent" widget="percentage"/> <field name="x_produk_aksesoris_sparepart"/> <field name="cache_reset_status"/> <field name="parent_id"/> |
