diff options
| -rwxr-xr-x | indoteknik_custom/__manifest__.py | 2 | ||||
| -rw-r--r-- | indoteknik_custom/models/account_move.py | 28 | ||||
| -rw-r--r-- | indoteknik_custom/models/approval_payment_term.py | 3 | ||||
| -rw-r--r-- | indoteknik_custom/models/res_partner.py | 11 | ||||
| -rwxr-xr-x | indoteknik_custom/models/sale_order.py | 24 | ||||
| -rw-r--r-- | indoteknik_custom/report/purchase_report.xml | 162 | ||||
| -rw-r--r-- | indoteknik_custom/views/res_partner.xml | 3 | ||||
| -rwxr-xr-x | indoteknik_custom/views/sale_order.xml | 9 |
8 files changed, 232 insertions, 10 deletions
diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index 31685005..09a3aa6f 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -166,6 +166,7 @@ 'report/report_invoice.xml', 'report/report_picking.xml', 'report/report_sale_order.xml', + 'report/purchase_report.xml', 'views/vendor_sla.xml', 'views/coretax_faktur.xml', 'views/public_holiday.xml', @@ -177,6 +178,7 @@ 'views/tukar_guling_po.xml', # 'views/refund_sale_order.xml', 'views/update_date_planned_po_wizard_view.xml', + # 'views/reimburse.xml', ], 'demo': [], 'css': [], diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index c93cfb76..70cd07e4 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -105,6 +105,34 @@ class AccountMove(models.Model): tracking=True ) + # def _check_and_lock_cbd(self): + # cbd_term = self.env['account.payment.term'].browse(26) + # today = date.today() + + # # Cari semua invoice overdue + # overdue_invoices = self.search([ + # ('move_type', '=', 'out_invoice'), + # ('state', '=', 'posted'), + # ('payment_state', 'not in', ['paid', 'in_payment', 'reversed']), + # ('invoice_date_due', '!=', False), + # ('invoice_date_due', '<=', today - timedelta(days=30)), + # ], limit=3) + + # _logger.info(f"Found {len(overdue_invoices)} overdue invoices for CBD lock check.") + # _logger.info(f"Overdue Invoices: {overdue_invoices.mapped('name')}") + + # # Ambil partner unik dari invoice + # partners_to_lock = overdue_invoices.mapped('partner_id').filtered(lambda p: not p.is_cbd_locked) + # _logger.info(f"Partners to lock: {partners_to_lock.mapped('name')}") + + # # Lock hanya partner yang belum locked + # if partners_to_lock: + # partners_to_lock.write({ + # 'is_cbd_locked': True, + # 'property_payment_term_id': cbd_term.id, + # }) + + def compute_partial_payment(self): for move in self: if move.amount_total_signed > 0 and move.amount_residual_signed > 0 and move.payment_state == 'partial': diff --git a/indoteknik_custom/models/approval_payment_term.py b/indoteknik_custom/models/approval_payment_term.py index 61339b99..7cab91f1 100644 --- a/indoteknik_custom/models/approval_payment_term.py +++ b/indoteknik_custom/models/approval_payment_term.py @@ -172,7 +172,8 @@ class ApprovalPaymentTerm(models.Model): 'blocking_stage': self.blocking_stage, 'warning_stage': self.warning_stage, 'active_limit': self.active_limit, - 'property_payment_term_id': self.property_payment_term_id.id + 'property_payment_term_id': self.property_payment_term_id.id, + 'is_locked_cbd': False, }) self.approve_date = datetime.utcnow() self.state = 'approved' diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index 148a3fd0..36570e8f 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -1,6 +1,6 @@ from odoo import models, fields, api from odoo.exceptions import UserError, ValidationError -from datetime import datetime +from datetime import datetime, timedelta from odoo.http import request import re import requests @@ -181,10 +181,8 @@ class ResPartner(models.Model): payment_difficulty = fields.Selection([('bermasalah', 'Bermasalah'),('sulit', 'Sulit'),('agak_sulit', 'Agak Sulit'),('normal', 'Normal')], string='Payment Difficulty', tracking=3) payment_history_url = fields.Text(string='Payment History URL') - # no compute - # payment_diff = fields.Selection([('bermasalah', 'Bermasalah'),('sulit', 'Sulit'),('agak_sulit', 'Agak Sulit'),('normal', 'Normal')], string='Payment Difficulty', tracking=3) - - # tidak terpakai + is_cbd_locked = fields.Boolean("Locked to CBD?", default=False, tracking=True, help="Jika dicentang, maka partner ini terkunci pada payment term CBD karena memiliki invoice yang sudah jatuh tempo lebih dari 30 hari.") + @api.model def _default_payment_term(self): @@ -193,9 +191,10 @@ class ResPartner(models.Model): property_payment_term_id = fields.Many2one( 'account.payment.term', string='Payment Terms', - default=_default_payment_term + default=_default_payment_term, tracking=3 ) + @api.depends("street", "street2", "city", "state_id", "country_id", "blok", "nomor", "rt", "rw", "kelurahan_id", "kecamatan_id") def _alamat_lengkap_text(self): diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 9952af9a..76d4d7e7 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -393,6 +393,26 @@ class SaleOrder(models.Model): ('paid', 'Full Paid'), ('no_invoice', 'No Invoice'), ], string="Payment Status Invoice", compute="_compute_payment_state_custom", store=False) + partner_is_cbd_locked = fields.Boolean( + string="Partner Locked CBD", + compute="_compute_partner_is_cbd_locked" + ) + + @api.depends('partner_id.is_cbd_locked') + def _compute_partner_is_cbd_locked(self): + for order in self: + order.partner_is_cbd_locked = order.partner_id.is_cbd_locked + + + @api.constrains('payment_term_id', 'partner_id', 'state') + def _check_cbd_lock_sale_order(self): + # cbd_term = self.env['account.payment.term'].browse(26) + for rec in self: + if rec.state == 'draft' and rec.partner_id.is_cbd_locked: + # if rec.payment_term_id and rec.payment_term_id != cbd_term: + raise ValidationError( + "Customer ini terkunci ke CBD, hanya boleh pakai Payment Term CBD." + ) @api.depends('invoice_ids.payment_state', 'invoice_ids.amount_total', 'invoice_ids.amount_residual') def _compute_payment_state_custom(self): @@ -2115,8 +2135,8 @@ class SaleOrder(models.Model): if self.payment_term_id.id == 31 and self.total_percent_margin < 25: raise UserError("Jika ingin menggunakan Tempo 90 Hari maka margin harus di atas 25%") - if self.warehouse_id.id != 8 and self.warehouse_id.id != 10: # GD Bandengan - raise UserError('Gudang harus Bandengan') + if self.warehouse_id.id != 8 and self.warehouse_id.id != 10 and self.warehouse_id.id != 12: # GD Bandengan / Pameran + raise UserError('Gudang harus Bandengan atau Pameran') if self.state not in ['draft', 'sent']: raise UserError("Status harus draft atau sent") diff --git a/indoteknik_custom/report/purchase_report.xml b/indoteknik_custom/report/purchase_report.xml new file mode 100644 index 00000000..9d7f4028 --- /dev/null +++ b/indoteknik_custom/report/purchase_report.xml @@ -0,0 +1,162 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <data> + <!-- Report Action --> + <record id="action_report_purchaseorder_website" model="ir.actions.report"> + <field name="name">Purchase Order (Website)</field> + <field name="model">purchase.order</field> + <field name="report_type">qweb-pdf</field> + <field name="report_name">indoteknik_custom.report_purchaseorder_website</field> + <field name="report_file">indoteknik_custom.report_purchaseorder_website</field> + <field name="print_report_name"> + ('PO - %s - %s' % (object.partner_id.name, object.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_website"> + <t t-call="web.html_container"> + <t t-foreach="docs" t-as="doc"> + <t t-call="indoteknik_custom.report_purchaseorder_website_document" t-lang="doc.partner_id.lang"/> + </t> + </t> + </template> + + <!-- Document Template --> +<template id="report_purchaseorder_website_document"> + <t t-call="web.external_layout"> + <t t-set="doc" t-value="doc.with_context(lang=doc.partner_id.lang)" /> + + <div class="page"> + <!-- Header Image --> + <div class="mb16"> + <img src="https://erp.indoteknik.com/api/image/ir.attachment/datas/2498521" + style="width:100%; max-height:100px; object-fit:contain;"/> + </div> + + <!-- Title --> + <h2 class="text-center mb4" style="color:#d32f2f; font-weight:bold;"> + PURCHASE ORDER + </h2> + <h4 class="text-center mb32"> + No. <span t-field="doc.name"/> + </h4> + + <!-- Top Info sejajar --> + <div class="row mb16" style="font-size:12px;"> + <div class="col-4"> + <strong>Term Of Payment:</strong> + <span t-field="doc.payment_term_id.name"/> + </div> + <div class="col-4"> + <strong>Order Date:</strong> + <span t-field="doc.date_order" t-options='{"widget": "date"}'/> + </div> + <div class="col-4"> + <strong>Responsible:</strong> + <span t-field="doc.user_id"/> + </div> + </div> + + <!-- Vendor & Shipping Info sejajar --> + <div class="row mb32" style="font-size:12px;"> + <div class="col-6" style="border:1px solid #ccc; padding:8px;"> + <strong>Alamat Pengiriman:</strong><br/> + PT Indoteknik (Bandengan 1 Depan)<br/> + Jl. Bandengan Utara Komp A 8 B<br/> + RT. Penjaringan, Kec. Penjaringan, Jakarta (BELAKANG INDOMARET)<br/> + JK 14440<br/> + Indonesia + </div> + <div class="col-6" style="border:1px solid #ccc; padding:8px;"> + <strong>Nama Vendor:</strong><br/> + <div t-field="doc.partner_id" + t-options='{"widget": "contact", "fields": ["address", "name", "phone"], + "no_marker": True, "phone_icons": True}'/> + </div> + </div> + + <!-- Order Lines --> + <table class="table table-sm o_main_table" style="font-size:11px; border:1px solid #000; border-collapse: collapse; width:100%;"> + <thead style="display: table-row-group; background:#f5f5f5;"> + <tr> + <th style="border:1px solid #000; padding:4px;">Description</th> + <th class="text-right" style="border:1px solid #000; padding:4px;">Quantity</th> + <th class="text-right" style="border:1px solid #000; padding:4px;">Unit Price</th> + <th class="text-right" style="border:1px solid #000; padding:4px;">Taxes</th> + <th class="text-right" style="border:1px solid #000; padding:4px;">Subtotal</th> + </tr> + </thead> + <tbody> + <t t-foreach="doc.order_line" t-as="line"> + <!-- Main row --> + <tr> + <td style="border:1px solid #000; padding:4px;"> + <span t-field="line.name"/> + </td> + <td class="text-right" style="border:1px solid #000; padding:4px;"> + <span t-field="line.product_qty"/> + <span t-field="line.product_uom"/> + </td> + <td class="text-right" style="border:1px solid #000; padding:4px;"> + <span t-field="line.price_unit"/> + </td> + <td class="text-right" style="border:1px solid #000; padding:4px;"> + <span t-esc="', '.join(map(lambda x: (x.description or x.name), line.taxes_id))"/> + </td> + <td class="text-right" style="border:1px solid #000; padding:4px;"> + <span t-field="line.price_subtotal"/> + </td> + </tr> + <!-- Website Description row --> + <t t-if="line.product_id.website_description"> + <tr> + <td colspan="5" style="border:1px solid #000; padding:6px; font-size:10px; color:#555;"> + <div t-raw="line.product_id.website_description"/> + </td> + </tr> + </t> + </t> + </tbody> + </table> + + <!-- Totals --> + <div class="clearfix"> + <div class="row"> + <div class="col-4 ml-auto"> + <table class="table table-sm" style="border:1px solid #000; border-collapse: collapse; width:100%;"> + <tr> + <td style="border:1px solid #000; padding:4px;"><strong>Subtotal</strong></td> + <td class="text-right" style="border:1px solid #000; padding:4px;"> + <span t-field="doc.amount_untaxed"/> + </td> + </tr> + <tr> + <td style="border:1px solid #000; padding:4px;">Taxes</td> + <td class="text-right" style="border:1px solid #000; padding:4px;"> + <span t-field="doc.amount_tax"/> + </td> + </tr> + <tr class="o_total"> + <td style="border:1px solid #000; padding:4px;"><strong>Total</strong></td> + <td class="text-right" style="border:1px solid #000; padding:4px;"> + <span t-field="doc.amount_total"/> + </td> + </tr> + </table> + </div> + </div> + </div> + + <!-- Notes --> + <div class="mt32" style="font-size:11px;"> + <p t-field="doc.notes"/> + </div> + </div> + </t> + </template> + +</odoo> diff --git a/indoteknik_custom/views/res_partner.xml b/indoteknik_custom/views/res_partner.xml index ca1a36de..7d5be3b7 100644 --- a/indoteknik_custom/views/res_partner.xml +++ b/indoteknik_custom/views/res_partner.xml @@ -21,6 +21,7 @@ <field name="reference_number"/> </field> <field name="property_payment_term_id" position="after"> + <field name="is_cbd_locked" readonly="1"/> <field name="user_payment_terms_sales" readonly="1"/> <field name="date_payment_terms_sales" readonly="1"/> </field> @@ -107,7 +108,7 @@ <field name="reminder_invoices"/> </xpath> <xpath expr="//field[@name='property_payment_term_id']" position="attributes"> - <attribute name="readonly">0</attribute> + <attribute name="readonly">1</attribute> </xpath> <xpath expr="//field[@name='property_supplier_payment_term_id']" position="attributes"> <attribute name="readonly">1</attribute> diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index 156c48d7..326fd560 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -41,6 +41,15 @@ string="Refund" class="btn-primary" /> </xpath> + <xpath expr="//sheet" position="before"> + <field name="partner_is_cbd_locked" invisible="1"/> + <div class="alert alert-danger" + role="alert" + style="height: 40px; margin-bottom:0px;" + attrs="{'invisible':['|', ('partner_is_cbd_locked','=',False), ('state', 'not in', ['draft', 'cancel'])]}"> + <strong>Warning!</strong> Payment Terms Customer terkunci menjadi <b>Cash Before Delivery (C.B.D.)</b> karena ada invoice telah jatuh tempo <b>30 hari</b>. Silakan ajukan <b>Approval Payment Term</b> untuk membuka kunci. + </div> + </xpath> <div class="oe_button_box" name="button_box"> <field name="advance_payment_move_ids" invisible="1"/> <button name="action_open_advance_payment_moves" |
