diff options
| author | Indoteknik . <it@fixcomart.co.id> | 2025-07-05 15:00:30 +0700 |
|---|---|---|
| committer | Indoteknik . <it@fixcomart.co.id> | 2025-07-05 15:00:30 +0700 |
| commit | 2b7d3e4f575c881b73f18b242dbf65e9bbf4c5ab (patch) | |
| tree | fe9d5aca45b030cfedcc8875c02bafcb277d8f3f | |
| parent | 20f206f3d9b798fee50a06d4a462cf256a71d58e (diff) | |
| parent | 5b1b45d46e34c6724572b9b3182813e0bfdea0a3 (diff) | |
(andri) fix conflict
24 files changed, 588 insertions, 145 deletions
diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index da18e16a..0525adc3 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -8,6 +8,8 @@ import logging _logger = logging.getLogger(__name__) +_logger = logging.getLogger(__name__) + class StockPicking(controller.Controller): prefix = '/api/v1/' PREFIX_PARTNER = prefix + 'partner/<partner_id>/' @@ -145,7 +147,6 @@ class StockPicking(controller.Controller): 'name': picking_data.name }) - @http.route(prefix + 'webhook/biteship', type='json', auth='public', methods=['POST'], csrf=False) def update_status_from_biteship(self, **kw): _logger.info("Biteship Webhook: Request received at controller start (type='json').") @@ -180,18 +181,6 @@ class StockPicking(controller.Controller): # Untuk error, kembalikan dictionary error juga, Odoo akan mengonversinya ke JSON return {'status': 'error', 'message': str(e)} - # @http.route(prefix + 'webhook/biteship', auth='public', methods=['POST'], csrf=False) - # def udpate_status_from_bitehsip(self, **kw): - # return "ok" - - # def process_order_status(self, data): - # picking_model = request.env['stock.picking'].sudo().search([('biteship_id', '=', data.get('order_id'))], - # limit=1) - # if data.get('status') == 'picked': - # picking_model.write({'driver_departure_date': datetime.utcnow()}) - # elif data.get('status') == 'delivered': - # picking_model.write({'driver_arrival_date': datetime.utcnow()}) - def process_order_status(self, data): picking = request.env['stock.picking'].sudo().search([ ('biteship_id', '=', data.get('order_id')) diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index ad019d4b..17cec7b6 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -156,6 +156,7 @@ 'views/stock_backorder_confirmation_views.xml', 'views/barcoding_product.xml', 'views/project_views.xml', + 'views/approval_payment_term.xml', 'report/report.xml', 'report/report_banner_banner.xml', 'report/report_banner_banner2.xml', diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index 3f538e25..b815b472 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -151,4 +151,5 @@ from . import account_payment_register from . import stock_inventory from . import sale_order_delay from . import approval_invoice_date +from . import approval_payment_term # from . import patch diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 66020a69..b6627867 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -70,6 +70,7 @@ class AccountMove(models.Model): reklas_misc_id = fields.Many2one('account.move', string='Journal Entries Reklas') # Di model account.move bill_id = fields.Many2one('account.move', string='Vendor Bill', domain=[('move_type', '=', 'in_invoice')], help='Bill asal dari proses reklas ini') + down_payment = fields.Boolean('Down Payments?') # def name_get(self): @@ -98,12 +99,27 @@ class AccountMove(models.Model): self.invoice_date = self.date + # def compute_length_of_payment(self): + # for rec in self: + # 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 + # else: + # rec.length_of_payment = 0 + def compute_length_of_payment(self): for rec in self: - payment_term = rec.invoice_payment_term_id.line_ids[0].days + payment_term = 0 + if rec.invoice_payment_term_id and rec.invoice_payment_term_id.line_ids: + 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 @@ -436,18 +452,18 @@ class AccountMove(models.Model): return invoices def export_faktur_to_xml(self): - valid_invoices = self - # Panggil model coretax.faktur untuk menghasilkan XML coretax_faktur = self.env['coretax.faktur'].create({}) - response = coretax_faktur.export_to_download(invoices=valid_invoices) - current_time = datetime.utcnow() - # Tandai faktur sebagai sudah diekspor + response = coretax_faktur.export_to_download( + invoices=valid_invoices, + down_payments=[inv.down_payment for inv in valid_invoices], + ) + valid_invoices.write({ 'is_efaktur_exported': True, - 'date_efaktur_exported': current_time, # Set tanggal ekspor + 'date_efaktur_exported': datetime.utcnow(), }) - return response + return response
\ No newline at end of file diff --git a/indoteknik_custom/models/approval_payment_term.py b/indoteknik_custom/models/approval_payment_term.py new file mode 100644 index 00000000..81eb1908 --- /dev/null +++ b/indoteknik_custom/models/approval_payment_term.py @@ -0,0 +1,82 @@ +from odoo import models, api, fields +from odoo.exceptions import AccessError, UserError, ValidationError +from datetime import timedelta, date, datetime +import logging + +_logger = logging.getLogger(__name__) + +class ApprovalPaymentTerm(models.Model): + _name = "approval.payment.term" + _description = "Approval Payment Term" + _inherit = ['mail.thread'] + _rec_name = 'number' + + number = fields.Char(string='Document No', index=True, copy=False, readonly=True, tracking=True) + partner_id = fields.Many2one('res.partner', string='Partner', copy=False) + property_payment_term_id = fields.Many2one('account.payment.term', string='Payment Term', copy=False, tracking=True) + parent_id = fields.Many2one('res.partner', string='Related Company', copy=False) + blocking_stage = fields.Float(string='Blocking Amount', + help="Cannot make sales once the selected " + "customer is crossed blocking amount." + "Set its value to 0.00 to disable " + "this feature", tracking=True, copy=False) + warning_stage = fields.Float(string='Warning Amount', + help="A warning message will appear once the " + "selected customer is crossed warning " + "amount. Set its value to 0.00 to" + " disable this feature", tracking=True, copy=False) + active_limit = fields.Boolean('Active Credit Limit', copy=False, tracking=True) + approve_sales_manager = fields.Boolean('Approve Sales Manager', tracking=True, copy=False) + approve_finance = fields.Boolean('Approve Finance', tracking=True, copy=False) + approve_leader = fields.Boolean('Approve Pimpinan', tracking=True, copy=False) + reason = fields.Text('Reason', tracking=True) + approve_date = fields.Datetime('Approve Date') + + + @api.constrains('partner_id') + def constrains_partner_id(self): + if self.partner_id: + self.parent_id = self.partner_id.parent_id.id if self.partner_id.parent_id else None + self.blocking_stage = self.partner_id.blocking_stage + self.warning_stage = self.partner_id.warning_stage + self.active_limit = self.partner_id.active_limit + self.property_payment_term_id = self.partner_id.property_payment_term_id.id + + def button_approve(self): + user = self.env.user + is_it = user.has_group('indoteknik_custom.group_role_it') + + if is_it or user.id == 19: + self.approve_sales_manager = True + return + + if is_it or user.id == 688 and self.approve_sales_manager: + self.approve_finance = True + return + + if is_it or user.id == 7 and self.approve_sales_manager and self.approve_finance: + self.approve_leader = True + + if not is_it or not self.approve_finance: + raise UserError('Harus Approval Finance!!') + if not is_it or not self.approve_leader: + raise UserError('Harus Approval Pimpinan!!') + + if user.id == 7: + if not self.approve_finance: + raise UserError('Belum Di Approve Oleh Finance') + + if self.approve_leader == True: + self.partner_id.write({ + 'blocking_stage': self.blocking_stage, + 'warning_stage': self.warning_stage, + 'active_limit': self.active_limit, + 'property_payment_term_id': self.property_payment_term_id.id + }) + self.approve_date = datetime.utcnow() + + @api.model + def create(self, vals): + vals['number'] = self.env['ir.sequence'].next_by_code('approval.payment.term') or '0' + result = super(ApprovalPaymentTerm, self).create(vals) + return result diff --git a/indoteknik_custom/models/automatic_purchase.py b/indoteknik_custom/models/automatic_purchase.py index c9edf07c..83a7cb3c 100644 --- a/indoteknik_custom/models/automatic_purchase.py +++ b/indoteknik_custom/models/automatic_purchase.py @@ -486,7 +486,7 @@ 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_onhand_bandengan + qty_purchase = point.product_max_qty - point.product_id.qty_incoming_bandengan - 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 03d32d2d..97184cdb 100644 --- a/indoteknik_custom/models/commision.py +++ b/indoteknik_custom/models/commision.py @@ -148,6 +148,7 @@ class CustomerCommision(models.Model): _order = 'id desc' _inherit = ['mail.thread'] _rec_name = 'number' + _description = 'Customer Benefits' number = fields.Char(string='Document No', index=True, copy=False, readonly=True) date_from = fields.Date(string='Date From', required=True) @@ -175,12 +176,22 @@ class CustomerCommision(models.Model): ('approved', 'Approved'), ('reject', 'Rejected'), ], string='Status') - commision_percent = fields.Float(string='Commision %', tracking=3) - commision_amt = fields.Float(string='Commision Amount', tracking=3) - cashback = fields.Float(string='Cashback', compute="compute_cashback") - total_commision = fields.Float(string='Total Commision', compute="compute_cashback") + + # commision_percent = fields.Float(string='Commision %', tracking=3) + commision_percent = fields.Float(string='Persentase (%)', tracking=3) + + # commision_amt = fields.Float(string='Commision Amount', tracking=3) + commision_amt = fields.Float(string='Amount', tracking=3) + + # cashback = fields.Float(string='Cashback', compute="compute_cashback") + cashback = fields.Float(string='PPh Cashback', compute="compute_cashback") + + # total_commision = fields.Float(string='Total Commision', compute="compute_cashback") + total_commision = fields.Float(string='Cashback yang dibayarkan', compute="compute_cashback") + total_cashback = fields.Float(string='Total Cashback') - commision_amt_text = fields.Char(string='Commision Amount Text', compute='compute_delivery_amt_text') + commision_amt_text = fields.Char(string='Amount Text', compute='compute_delivery_amt_text') + total_cashback_text = fields.Char(string='Cashback Text', compute='compute_total_cashback_text') total_dpp = fields.Float(string='Total DPP', compute='_compute_total_dpp') commision_type = fields.Selection([ ('fee', 'Fee'), @@ -271,6 +282,20 @@ class CustomerCommision(models.Model): except: record.commision_amt_text = res + def compute_total_cashback_text(self): + tb = Terbilang() + + for record in self: + res = '' + try: + if record.total_commision > 0: + tb.parse(int(record.total_commision)) + res = tb.getresult().title() + record.total_cashback_text = f"{res} Rupiah" if res else "" + except Exception as e: + record.total_cashback_text = "" + _logger.error("Error computing cashback text: %s", str(e)) + def _compute_grouped_numbers(self): for rec in self: so_numbers = set() @@ -348,14 +373,31 @@ class CustomerCommision(models.Model): @api.model def create(self, vals): - vals['number'] = self.env['ir.sequence'].next_by_code('customer.commision') or '0' - # if vals['commision_amt'] > 0: - # commision_amt = vals['commision_amt'] - # total_dpp = vals['total_dpp'] - # commision_percent = commision_amt / total_dpp * 100 - # vals['commision_percent'] = commision_percent - result = super(CustomerCommision, self).create(vals) - return result + commision_type = vals.get('commision_type') + + if commision_type == 'cashback': + sequence_code = 'customer.commision.cashback' + elif commision_type == 'fee': + sequence_code = 'customer.commision.fee' + elif commision_type == 'rebate': + sequence_code = 'customer.commision.rebate' + else: + raise UserError('Tipe komisi tidak dikenal!') + + vals['number'] = self.env['ir.sequence'].next_by_code(sequence_code) or '0' + + return super(CustomerCommision, self).create(vals) + + # @api.model + # def create(self, vals): + # vals['number'] = self.env['ir.sequence'].next_by_code('customer.commision') or '0' + # # if vals['commision_amt'] > 0: + # # commision_amt = vals['commision_amt'] + # # total_dpp = vals['total_dpp'] + # # commision_percent = commision_amt / total_dpp * 100 + # # vals['commision_percent'] = commision_percent + # result = super(CustomerCommision, self).create(vals) + # return result def action_confirm_customer_commision(self): jakarta_tz = pytz.timezone('Asia/Jakarta') diff --git a/indoteknik_custom/models/coretax_fatur.py b/indoteknik_custom/models/coretax_fatur.py index 92ff1a72..ce94306f 100644 --- a/indoteknik_custom/models/coretax_fatur.py +++ b/indoteknik_custom/models/coretax_fatur.py @@ -3,6 +3,9 @@ import xml.etree.ElementTree as ET from xml.dom import minidom import base64 import re +import logging + +_logger = logging.getLogger(__name__) class CoretaxFaktur(models.Model): @@ -32,7 +35,7 @@ class CoretaxFaktur(models.Model): return cleaned_number - def generate_xml(self, invoices=None): + def generate_xml(self, invoices=None, down_payments=False): # Buat root XML root = ET.Element('TaxInvoiceBulk', { 'xmlns:xsi': "http://www.w3.org/2001/XMLSchema-instance", @@ -72,59 +75,96 @@ class CoretaxFaktur(models.Model): ET.SubElement(tax_invoice, 'BuyerEmail').text = invoice.partner_id.email or '' ET.SubElement(tax_invoice, 'BuyerIDTKU').text = buyerIDTKU - # Filter product - product_lines = invoice.invoice_line_ids.filtered( - lambda l: not l.display_type and hasattr(l, 'account_id') and - l.account_id and l.product_id and - l.account_id.id != self.DISCOUNT_ACCOUNT_ID and - l.quantity != -1 - ) + _logger.info(" invoice down_payments: %s", invoice.down_payment) + # Handle product lines based on down_payments flag + if invoice.down_payment and invoice.invoice_origin: + # Get from sale.order.line for down payment + sale_order = invoice.sale_id + if sale_order: + product_lines = sale_order.order_line.filtered( + lambda l: l.product_id and not l.is_downpayment and not l.display_type and not l.product_id.id == 229625 + ) + # Convert sale order lines to invoice-like format + converted_lines = [] + for line in product_lines: + converted_lines.append({ + 'name': line.name, + 'product_id': line.product_id, + 'price_subtotal': line.price_subtotal, + 'quantity': line.product_uom_qty, + 'price_unit': line.price_unit, + 'account_id': line.order_id.analytic_account_id or False, + }) + product_lines = converted_lines + else: + product_lines = [] + else: + # Normal case - get from invoice lines + product_lines = invoice.invoice_line_ids.filtered( + lambda l: not l.display_type and hasattr(l, 'account_id') and + l.account_id and l.product_id and + l.account_id.id != self.DISCOUNT_ACCOUNT_ID and + l.quantity != -1 + ) - # Filter discount + # Filter discount (always from invoice) discount_lines = invoice.invoice_line_ids.filtered( lambda l: not l.display_type and ( - (hasattr(l, 'account_id') and l.account_id and - l.account_id.id == self.DISCOUNT_ACCOUNT_ID) or - (l.quantity == -1) + (hasattr(l, 'account_id') and l.account_id and + l.account_id.id == self.DISCOUNT_ACCOUNT_ID) or + (l.quantity == -1) ) ) - # Calculate total product amount (before discount) - total_product_amount = sum(line.price_subtotal for line in product_lines) + # Calculate totals + total_product_amount = sum(line.get('price_subtotal', 0) if isinstance(line, dict) + else line.price_subtotal for line in product_lines) if total_product_amount == 0: total_product_amount = 1 # Avoid division by zero - # Calculate total discount amount total_discount_amount = abs(sum(line.price_subtotal for line in discount_lines)) # Tambahkan elemen ListOfGoodService list_of_good_service = ET.SubElement(tax_invoice, 'ListOfGoodService') for line in product_lines: + # Handle both dict (converted sale lines) and normal invoice lines + if isinstance(line, dict): + line_price_subtotal = line['price_subtotal'] + line_quantity = line['quantity'] + line_name = line['name'] + line_price_unit = line['price_unit'] + else: + line_price_subtotal = line.price_subtotal + line_quantity = line.quantity + line_name = line.name + line_price_unit = line.price_unit + # Calculate prorated discount - line_proportion = line.price_subtotal / total_product_amount + line_proportion = line_price_subtotal / total_product_amount line_discount = total_discount_amount * line_proportion - # unit_price = line.price_unit - subtotal = line.price_subtotal - quantity = line.quantity + subtotal = line_price_subtotal + quantity = line_quantity total_discount = round(line_discount, 2) # Calculate other tax values otherTaxBase = round(subtotal * (11 / 12), 2) if subtotal else 0 vat_amount = round(otherTaxBase * 0.12, 2) + price_per_unit = round(subtotal / quantity, 2) if quantity else 0 + # Create the line in XML good_service = ET.SubElement(list_of_good_service, 'GoodService') ET.SubElement(good_service, 'Opt').text = 'A' ET.SubElement(good_service, 'Code').text = '000000' - ET.SubElement(good_service, 'Name').text = line.name + ET.SubElement(good_service, 'Name').text = line_name ET.SubElement(good_service, 'Unit').text = 'UM.0018' - ET.SubElement(good_service, 'Price').text = str(round(subtotal / quantity, 2)) if subtotal else '0' + # ET.SubElement(good_service, 'Price').text = str(round(line_price_unit, 2)) if line_price_unit else '0' + ET.SubElement(good_service, 'Price').text = str(price_per_unit) ET.SubElement(good_service, 'Qty').text = str(quantity) ET.SubElement(good_service, 'TotalDiscount').text = str(total_discount) - ET.SubElement(good_service, 'TaxBase').text = str( - round(subtotal)) if subtotal else '0' + ET.SubElement(good_service, 'TaxBase').text = str(round(subtotal)) if subtotal else '0' ET.SubElement(good_service, 'OtherTaxBase').text = str(otherTaxBase) ET.SubElement(good_service, 'VATRate').text = '12' ET.SubElement(good_service, 'VAT').text = str(vat_amount) @@ -136,9 +176,9 @@ class CoretaxFaktur(models.Model): xml_pretty = minidom.parseString(xml_str).toprettyxml(indent=" ") return xml_pretty - def export_to_download(self, invoices): + def export_to_download(self, invoices, down_payments): # Generate XML content - xml_content = self.generate_xml(invoices) + xml_content = self.generate_xml(invoices, down_payments) # Encode content to Base64 xml_encoded = base64.b64encode(xml_content.encode('utf-8')) diff --git a/indoteknik_custom/models/mrp_production.py b/indoteknik_custom/models/mrp_production.py index 14821f27..85b8405f 100644 --- a/indoteknik_custom/models/mrp_production.py +++ b/indoteknik_custom/models/mrp_production.py @@ -110,8 +110,9 @@ class MrpProduction(models.Model): 'picking_type_id': 28, # indoteknik bandengan receipts 'date_order': current_time, 'product_bom_id': self.product_id.id, - # 'sale_order_id': self.sale_order_id.id, - 'note_description': 'from Manufacturing Order' + 'sale_order_id': self.sale_order.id, + 'manufacturing_id': self.id, + 'note_description': 'from Manufacturing Order', } domain = [ diff --git a/indoteknik_custom/models/patch/http_override.py b/indoteknik_custom/models/patch/http_override.py index e1978edb..6bec1343 100644 --- a/indoteknik_custom/models/patch/http_override.py +++ b/indoteknik_custom/models/patch/http_override.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import odoo.http import json import logging diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 1a7e50f8..4dc26d74 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -97,46 +97,69 @@ class PurchaseOrder(models.Model): string="BU Related Count", compute='_compute_bu_related_count' ) + manufacturing_id = fields.Many2one('mrp.production', string='Manufacturing Orders') @api.depends('name') def _compute_bu_related_count(self): + StockPicking = self.env['stock.picking'] for order in self: if not order.name: order.bu_related_count = 0 continue - # BU langsung dari PO - base_bu = self.env['stock.picking'].search([ + # Ambil semua BU awal dari PO + base_bu = StockPicking.search([ ('name', 'ilike', 'BU/'), ('origin', 'ilike', order.name) ]) - base_names = base_bu.mapped('name') - # Return dari BU di atas - return_bu = self.env['stock.picking'].search([ - ('origin', 'in', [f"Return of {name}" for name in base_names]) - ]) + all_bu = base_bu + seen_names = set(base_bu.mapped('name')) + + # Loop rekursif untuk mencari seluruh return BU + while True: + next_bu = StockPicking.search([ + ('name', 'ilike', 'BU/'), + ('origin', 'in', ['Return of %s' % name for name in seen_names]) + ]) + next_names = set(next_bu.mapped('name')) + + if not next_names - seen_names: + break + + all_bu |= next_bu + seen_names |= next_names + + order.bu_related_count = len(all_bu) - order.bu_related_count = len(base_bu) + len(return_bu) def action_view_related_bu(self): self.ensure_one() + StockPicking = self.env['stock.picking'] + # Step 1: cari semua BU pertama (PUT, INT) yang berasal dari PO ini - base_bu = self.env['stock.picking'].search([ + base_bu = StockPicking.search([ ('name', 'ilike', 'BU/'), ('origin', 'ilike', self.name) ]) - base_bu_names = base_bu.mapped('name') - # Step 2: cari BU turunan (seperti BU/VRT) yang origin-nya mengandung nama BU tersebut - domain = [ - '|', - '&', - ('name', 'ilike', 'BU/'), - ('origin', 'ilike', self.name), - ('origin', 'in', [f"Return of {name}" for name in base_bu_names]) - ] + all_bu = base_bu + seen_names = set(base_bu.mapped('name')) + + # Step 2: Loop rekursif cari BU dengan origin 'Return of {name}' + while True: + next_bu = StockPicking.search([ + ('name', 'ilike', 'BU/'), + ('origin', 'in', ['Return of %s' % name for name in seen_names]) + ]) + next_names = set(next_bu.mapped('name')) + + if not next_names - seen_names: + break + + all_bu |= next_bu + seen_names |= next_names return { 'name': 'Related BU (INT/PRT/PUT/VRT)', @@ -144,7 +167,7 @@ class PurchaseOrder(models.Model): 'res_model': 'stock.picking', 'view_mode': 'tree,form', 'target': 'current', - 'domain': domain, + 'domain': [('id', 'in', list(all_bu.ids))], } diff --git a/indoteknik_custom/models/purchase_order_sales_match.py b/indoteknik_custom/models/purchase_order_sales_match.py index 0bd0092b..b18864f3 100644 --- a/indoteknik_custom/models/purchase_order_sales_match.py +++ b/indoteknik_custom/models/purchase_order_sales_match.py @@ -28,6 +28,20 @@ class PurchaseOrderSalesMatch(models.Model): purchase_price_po = fields.Float('Purchase Price PO', compute='_compute_purchase_price_po') purchase_line_id = fields.Many2one('purchase.order.line', string='Purchase Line', compute='_compute_purchase_line_id') hold_outgoing_so = fields.Boolean(string='Hold Outgoing SO', related='sale_id.hold_outgoing') + bu_pick = fields.Many2one('stock.picking', string='BU Pick', compute='compute_bu_pick') + + def compute_bu_pick(self): + for rec in self: + stock_move = self.env['stock.move'].search([ + ('reference', 'ilike', 'BU/PICK'), + ('state', 'in', ['confirmed','waiting','partially_available']), + ('product_id', '=', rec.product_id.id), + ('sale_line_id', '=', rec.sale_line_id.id), + ]) + if stock_move: + rec.bu_pick = stock_move.picking_id.id + else: + rec.bu_pick = None def _compute_purchase_line_id(self): for line in self: diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index 9986b9c0..f5347bea 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -164,6 +164,22 @@ class ResPartner(models.Model): "Set its value to 0.00 to disable " "this feature", tracking=3) telegram_id = fields.Char(string="Telegram") + avg_aging= fields.Float(string='Average Aging') + payment_difficulty = fields.Selection([('bermasalah', 'Bermasalah'),('sulit', 'Sulit'),('agak_sulit', 'Agak Sulit'),('normal', 'Normal')], string='Payment Difficulty', compute="_compute_payment_difficulty", inverse = "_inverse_payment_difficulty", tracking=3) + payment_history_url = fields.Text(string='Payment History URL') + + @api.depends('parent_id.payment_difficulty') + def _compute_payment_difficulty(self): + for partner in self: + if partner.parent_id: + partner.payment_difficulty = partner.parent_id.payment_difficulty + + def _inverse_payment_difficulty(self): + for partner in self: + if not partner.parent_id: + partner.child_ids.write({ + 'payment_difficulty': partner.payment_difficulty + }) @api.model def _default_payment_term(self): @@ -192,6 +208,10 @@ class ResPartner(models.Model): for rec in self: if 'latitude' in vals or 'longtitude' in vals: rec._update_address_from_coords() + + # Sinkronisasi payment_difficulty ke semua anak jika partner ini adalah parent + if not rec.parent_id and 'payment_difficulty' in vals: + rec.child_ids.write({'payment_difficulty': vals['payment_difficulty']}) # # # if 'property_payment_term_id' in vals: # # if not self.env.user.is_accounting and vals['property_payment_term_id'] != 26: @@ -210,6 +230,8 @@ class ResPartner(models.Model): for rec in records: if vals.get('latitude') and vals.get('longtitude'): rec._update_address_from_coords() + if rec.parent_id and not vals.get('payment_difficulty'): + rec.payment_difficulty = rec.parent_id.payment_difficulty return records @api.constrains('name') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 6da46398..591951ca 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -382,13 +382,15 @@ class SaleOrder(models.Model): # Simpan ke field sebagai UTC-naive datetime (standar Odoo) order.et_products = eta_datetime.astimezone(pytz.utc).replace(tzinfo=None) - @api.depends('picking_ids.state', 'picking_ids.date_reserved') + @api.depends('picking_ids.state', 'picking_ids.date_done') def _compute_eta_date_reserved(self): for order in self: pickings = order.picking_ids.filtered( - lambda p: p.state == 'assigned' and p.date_reserved and 'BU/PICK/' in (p.name or '') + lambda p: p.state in ('assigned', 'done') and p.date_reserved and 'BU/PICK/' in (p.name or '') ) - order.eta_date_reserved = min(pickings.mapped('date_reserved')) if pickings else False + done_dates = [d for d in pickings.mapped('date_done') if d] + order.eta_date_reserved = min(done_dates) if done_dates else False + # order.eta_date_reserved = min(pickings.mapped('date_done')) if pickings else False @api.onchange('shipping_cost_covered') def _onchange_shipping_cost_covered(self): @@ -591,11 +593,11 @@ class SaleOrder(models.Model): if shipping_option.exists(): courier_service = shipping_option.courier_service_code vals['delivery_service_type'] = courier_service - _logger.info("🛰️ Set delivery_service_type: %s from shipping_option_id: %s", courier_service, shipping_option_id) + _logger.info("Set delivery_service_type: %s from shipping_option_id: %s", courier_service, shipping_option_id) else: - _logger.warning("⚠️ shipping_option_id %s not found or invalid.", shipping_option_id) + _logger.warning("shipping_option_id %s not found or invalid.", shipping_option_id) else: - _logger.info("ℹ️ shipping_option_id not found in vals or record.") + _logger.info("shipping_option_id not found in vals or record.") # @api.model # def fields_get(self, allfields=None, attributes=None): @@ -845,25 +847,36 @@ class SaleOrder(models.Model): if total_weight == 0: raise UserError("Tidak dapat mengestimasi ongkir tanpa berat yang valid.") - destination_subsdistrict_id = self.real_shipping_id.kecamatan_id.rajaongkir_id + kecamatan_name = self.real_shipping_id.kecamatan_id.name + kota_name = self.real_shipping_id.kota_id.name + kelurahan_name = self.real_shipping_id.kelurahan_id.name + + destination_subsdistrict_id = self._get_subdistrict_id_from_komerce(kecamatan_name, kota_name, kelurahan_name) + + # destination_subsdistrict_id = self.real_shipping_id.kecamatan_id.rajaongkir_id if not destination_subsdistrict_id: raise UserError("Gagal mendapatkan ID kota tujuan.") result = self._call_rajaongkir_api(total_weight, destination_subsdistrict_id) + if not result or not result.get('data'): + raise UserError(_("Kurir %s tidak tersedia untuk tujuan ini. Silakan pilih kurir lain.") % self.carrier_id.name) + if result: shipping_options = [] - for courier in result['rajaongkir']['results']: - for cost_detail in courier['costs']: - service = cost_detail['service'] - description = cost_detail['description'] - etd = cost_detail['cost'][0]['etd'] - value = cost_detail['cost'][0]['value'] - shipping_options.append((service, description, etd, value, courier['code'])) - + + for cost in result.get('data', []): + service = cost.get('service') + description = cost.get('description') + etd = cost.get('etd', '') + value = cost.get('cost', 0) + provider = cost.get('code') + + shipping_options.append((service, description, etd, value, provider)) + self.env["shipping.option"].search([('sale_order_id', '=', self.id)]).unlink() _logger.info(f"Shipping options: {shipping_options}") - + for service, description, etd, value, provider in shipping_options: self.env["shipping.option"].create({ "name": service, @@ -873,19 +886,15 @@ class SaleOrder(models.Model): "sale_order_id": self.id, }) - self.shipping_option_id = self.env["shipping.option"].search([('sale_order_id', '=', self.id)], limit=1).id _logger.info(f"Shipping option SO ID: {self.shipping_option_id}") self.message_post( body=f"Estimasi Ongkos Kirim: Rp{self.delivery_amt}<br/>Detail Lain:<br/>" - f"{'<br/>'.join([f'Service: {s[0]}, Description: {s[1]}, ETD: {s[2]} hari, Cost: Rp {s[3]}' for s in shipping_options])}", + f"{'<br/>'.join([f'Service: {s[0]}, Description: {s[1]}, ETD: {s[2]}, Cost: Rp {s[3]}' for s in shipping_options])}", message_type="comment" ) - - # self.message_post(body=f"Estimasi Ongkos Kirim: Rp{self.delivery_amt}<br/>Detail Lain:<br/>{'<br/>'.join([f'Service: {s[0]}, Description: {s[1]}, ETD: {s[2]} hari, Cost: Rp {s[3]}' for s in shipping_options])}", message_type="comment") - else: raise UserError("Gagal mendapatkan estimasi ongkir.") @@ -1191,25 +1200,30 @@ class SaleOrder(models.Model): def _call_rajaongkir_api(self, total_weight, destination_subsdistrict_id): - url = 'https://pro.rajaongkir.com/api/cost' + url = 'https://rajaongkir.komerce.id/api/v1/calculate/domestic-cost' headers = { 'key': '9b1310f644056d84d60b0af6bb21611a', } courier = self.carrier_id.name.lower() data = { - 'origin': 2127, - 'originType': 'subdistrict', + 'origin': 17656, + # 'originType': 'subdistrict', 'destination': int(destination_subsdistrict_id), - 'destinationType': 'subdistrict', + # 'destinationType': 'subdistrict', 'weight': int(total_weight * 1000), 'courier': courier, } - response = requests.post(url, headers=headers, data=data) - if response.status_code == 200: - return response.json() - return None + try: + _logger.info(f"Calling RajaOngkir API with data: {data}") + response = requests.post(url, headers=headers, data=data) + _logger.info(f"RajaOngkir response: {response.status_code} - {response.text}") + + if response.status_code == 200: + return response.json() + except Exception as e: + _logger.error(f"Exception while calling RajaOngkir: {str(e)}") def _normalize_city_name(self, city_name): city_name = city_name.lower() @@ -1223,37 +1237,82 @@ class SaleOrder(models.Model): return city_name - def _get_city_id_by_name(self, city_name): - url = 'https://pro.rajaongkir.com/api/city' + # def _get_city_id_by_name(self, city_name): + # url = 'https://pro.rajaongkir.com/api/city' + # headers = { + # 'key': '9b1310f644056d84d60b0af6bb21611a', + # } + + # normalized_city_name = self._normalize_city_name(city_name) + + # response = requests.get(url, headers=headers) + # if response.status_code == 200: + # city_data = response.json() + # for city in city_data['rajaongkir']['results']: + # if city['city_name'].lower() == normalized_city_name: + # return city['city_id'] + # return None + + # def _get_subdistrict_id_by_name(self, city_id, subdistrict_name): + # url = f'https://pro.rajaongkir.com/api/subdistrict?city={city_id}' + # headers = { + # 'key': '9b1310f644056d84d60b0af6bb21611a', + # } + + # response = requests.get(url, headers=headers) + # if response.status_code == 200: + # subdistrict_data = response.json() + # for subdistrict in subdistrict_data['rajaongkir']['results']: + # subsdistrict_1 = subdistrict['subdistrict_name'].lower() + # subsdistrict_2 = subdistrict_name.lower() + + # if subsdistrict_1 == subsdistrict_2: + # return subdistrict['subdistrict_id'] + # return None + + def _get_subdistrict_id_from_komerce(self, kecamatan_name, kota_name, kelurahan_name=None): + url = 'https://rajaongkir.komerce.id/api/v1/destination/domestic-destination' headers = { 'key': '9b1310f644056d84d60b0af6bb21611a', } - normalized_city_name = self._normalize_city_name(city_name) - - response = requests.get(url, headers=headers) - if response.status_code == 200: - city_data = response.json() - for city in city_data['rajaongkir']['results']: - if city['city_name'].lower() == normalized_city_name: - return city['city_id'] - return None + if kelurahan_name: + search = f"{kelurahan_name} {kecamatan_name} {kota_name}" + else: + search = f"{kecamatan_name} {kota_name}" - def _get_subdistrict_id_by_name(self, city_id, subdistrict_name): - url = f'https://pro.rajaongkir.com/api/subdistrict?city={city_id}' - headers = { - 'key': '9b1310f644056d84d60b0af6bb21611a', + params = { + 'search': search, + 'limit': 5 } - response = requests.get(url, headers=headers) - if response.status_code == 200: - subdistrict_data = response.json() - for subdistrict in subdistrict_data['rajaongkir']['results']: - subsdistrict_1 = subdistrict['subdistrict_name'].lower() - subsdistrict_2 = subdistrict_name.lower() + try: + response = requests.get(url, headers=headers, params=params, timeout=10) + if response.status_code == 200: + data = response.json().get('data', []) + _logger.info(f"[Komerce] Fetched {len(data)} subdistricts for search '{search}'") + _logger.info(f"[Komerce] Response: {data}") + + normalized_kota = self._normalize_city_name(kota_name) + + for item in data: + match_kelurahan = ( + not kelurahan_name or + item.get('subdistrict_name', '').lower() == kelurahan_name.lower() + ) + if ( + match_kelurahan and + item.get('district_name', '').lower() == kecamatan_name.lower() and + item.get('city_name', '').lower() == normalized_kota + ): + return item.get('id') + + _logger.warning(f"[Komerce] No match for '{kecamatan_name}' in city '{kota_name}' with kelurahan '{kelurahan_name}'") + else: + _logger.error(f"[Komerce] HTTP Error {response.status_code}: {response.text}") + except Exception as e: + _logger.error(f"[Komerce] Exception: {e}") - if subsdistrict_1 == subsdistrict_2: - return subdistrict['subdistrict_id'] return None def _compute_type_promotion(self): @@ -1592,6 +1651,7 @@ class SaleOrder(models.Model): 'campaign_id': self.campaign_id.id, 'medium_id': self.medium_id.id, 'source_id': self.source_id.id, + 'down_payment': 229625 in [line.product_id.id for line in self.order_line], 'user_id': self.user_id.id, 'sale_id': self.id, 'invoice_user_id': self.user_id.id, @@ -1993,9 +2053,9 @@ class SaleOrder(models.Model): confirmed_bom = search_bom.filtered(lambda x: x.state == 'confirmed' or x.state == 'done') if not confirmed_bom: raise UserError( - "Product BOM belum dikonfirmasi di Manufacturing Orders. Silakan hubungi MD.") + "Product BOM belum dikonfirmasi di Manufacturing Orders. Silakan hubungi Purchasing.") else: - raise UserError("Product BOM tidak di temukan di manufacturing orders, silahkan hubungi MD") + raise UserError("Product BOM tidak di temukan di manufacturing orders, silahkan hubungi Purchasing") def check_duplicate_product(self): for order in self: diff --git a/indoteknik_custom/models/sale_orders_multi_update.py b/indoteknik_custom/models/sale_orders_multi_update.py index 95cfde21..962f60b5 100644 --- a/indoteknik_custom/models/sale_orders_multi_update.py +++ b/indoteknik_custom/models/sale_orders_multi_update.py @@ -11,6 +11,13 @@ class SaleOrdersMultiUpdate(models.TransientModel): sale_ids = self._context['sale_ids'] sales = self.env['sale.order'].browse(sale_ids) sales.action_multi_update_invoice_status() + + for sale in sales: + sale.message_post( + body="Sales Order has been marked as Completed", + message_type="comment" + ) + return { 'type': 'ir.actions.client', 'tag': 'display_notification', diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index d4167609..d25208d1 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1316,7 +1316,6 @@ class StockPicking(models.Model): self.final_seq = 0 self.set_picking_code_out() self.send_koli_to_so() - if (self.state_reserve == 'done' and self.picking_type_code == 'internal' and 'BU/PICK/' in self.name and self.linked_manual_bu_out): if not self.linked_manual_bu_out.date_reserved: @@ -1353,7 +1352,20 @@ class StockPicking(models.Model): 'target': 'new', } self.send_mail_bills() + if 'BU/PUT' in self.name: + self.automatic_reserve_product() return res + + def automatic_reserve_product(self): + if self.state == 'done': + po = self.env['purchase.order'].search([ + ('name', '=', self.group_id.name) + ]) + + for line in po.order_sales_match_line: + if not line.bu_pick: + continue + line.bu_pick.action_assign() def check_invoice_date(self): for picking in self: diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index 3dabae6d..2b970cfd 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -182,3 +182,4 @@ access_sale_order_delay,sale.order.delay,model_sale_order_delay,,1,1,1,1 access_production_purchase_match,access.production.purchase.match,model_production_purchase_match,,1,1,1,1 access_image_carousel,access.image.carousel,model_image_carousel,,1,1,1,1 access_v_sale_notin_matchpo,access.v.sale.notin.matchpo,model_v_sale_notin_matchpo,,1,1,1,1 +access_approval_payment_term,access.approval.payment.term,model_approval_payment_term,,1,1,1,1 diff --git a/indoteknik_custom/views/account_move.xml b/indoteknik_custom/views/account_move.xml index 0fc62293..2f52b3d9 100644 --- a/indoteknik_custom/views/account_move.xml +++ b/indoteknik_custom/views/account_move.xml @@ -37,6 +37,8 @@ </field> <field name="ref" position="after"> <field name="sale_id" readonly="1" attrs="{'invisible': [('move_type', '!=', 'entry')]}"/> + </field> + <field name="reklas_misc_id" position="after"> <field name="purchase_order_id" context="{'form_view_ref': 'purchase.purchase_order_form'}" options="{'no_create': True}"/> <field name="bill_id" readonly="1"/> </field> @@ -60,6 +62,7 @@ <field name="due_extension"/> <field name="counter"/> <field name="nomor_kwitansi"/> + <field name="down_payment"/> </field> <field name="to_check" position="after"> <field name="already_paid"/> @@ -67,6 +70,7 @@ <field name="so_shipping_covered_by"/> <field name="so_delivery_amt"/> <field name="flag_delivery_amt"/> + <field name="length_of_payment"/> </field> <field name="amount_untaxed" position="after"> <field name="other_subtotal" invisible="1"/> diff --git a/indoteknik_custom/views/approval_payment_term.xml b/indoteknik_custom/views/approval_payment_term.xml new file mode 100644 index 00000000..87c77385 --- /dev/null +++ b/indoteknik_custom/views/approval_payment_term.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<odoo> + <record id="approval_payment_term_tree" model="ir.ui.view"> + <field name="name">approval.payment.term.tree</field> + <field name="model">approval.payment.term</field> + <field name="arch" type="xml"> + <tree default_order="create_date desc"> + <field name="number"/> + <field name="partner_id"/> + <field name="parent_id"/> + <field name="property_payment_term_id"/> + <field name="approve_date" optional="hide"/> + <field name="approve_sales_manager" optional="hide"/> + <field name="approve_finance" optional="hide"/> + <field name="approve_leader" optional="hide"/> + <field name="create_uid" optional="hide"/> + </tree> + </field> + </record> + + <record id="approval_payment_term_form" model="ir.ui.view"> + <field name="name">approval.payment.term.form</field> + <field name="model">approval.payment.term</field> + <field name="arch" type="xml"> + <form> + <header> + <button name="button_approve" + string="Approve" + type="object" + attrs="{'invisible': [('approve_leader', '=', True)]}" + /> + </header> + <sheet string="Approval Payment Term"> + <group> + <group> + <field name="number" readonly="1"/> + <field name="partner_id"/> + <field name="parent_id" readonly="1"/> + <field name="blocking_stage" attrs="{'readonly': [('number', '=', False)]}"/> + <field name="warning_stage" attrs="{'readonly': [('number', '=', False)]}"/> + <field name="property_payment_term_id" attrs="{'readonly': [('number', '=', False)]}"/> + <field name="active_limit" attrs="{'readonly': [('number', '=', False)]}"/> + </group> + <group> + <field name="reason"/> + <field name="approve_date" readonly="1"/> + <field name="approve_sales_manager" readonly="1"/> + <field name="approve_finance" readonly="1"/> + <field name="approve_leader" readonly="1"/> + </group> + </group> + </sheet> + <div class="oe_chatter"> + <field name="message_follower_ids" widget="mail_followers"/> + <field name="message_ids" widget="mail_thread"/> + </div> + </form> + </field> + </record> + + <record id="approval_payment_term_action" model="ir.actions.act_window"> + <field name="name">Approval Payment Term</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">approval.payment.term</field> + <field name="view_mode">tree,form</field> + </record> + + <menuitem id="menu_approval_payment_term" name="Approval Payment Term" + parent="account.menu_finance_receivables" + action="approval_payment_term_action" + sequence="100" + /> + +</odoo>
\ No newline at end of file diff --git a/indoteknik_custom/views/customer_commision.xml b/indoteknik_custom/views/customer_commision.xml index 4be0840f..1c17bf63 100644 --- a/indoteknik_custom/views/customer_commision.xml +++ b/indoteknik_custom/views/customer_commision.xml @@ -70,7 +70,7 @@ statusbar_visible="draft,pengajuan1,pengajuan2,pengajuan3,pengajuan4,approved" statusbar_colors='{"reject":"red"}'/> </header> - <sheet string="Customer Commision"> + <sheet string="Customer Benefits"> <div class="oe_button_box" name="button_box"/> <group> <group> @@ -83,6 +83,7 @@ <field name="commision_amt_text"/> <field name="cashback" attrs="{'invisible': [('commision_type', 'not in', ['cashback'])]}"/> <field name="total_commision" attrs="{'invisible': [('commision_type', 'not in', ['cashback'])]}"/> + <field name="total_cashback_text" attrs="{'invisible': [('commision_type', 'not in', ['cashback'])]}"/> <field name="grouped_so_number" readonly="1"/> <field name="grouped_invoice_number" readonly="1"/> <field name="approved_by" readonly="1"/> @@ -173,7 +174,7 @@ </record> <record id="customer_commision_action" model="ir.actions.act_window"> - <field name="name">Customer Commision</field> + <field name="name">Customer Benefits</field> <field name="type">ir.actions.act_window</field> <field name="res_model">customer.commision</field> <field name="search_view_id" ref="view_customer_commision_filter"/> @@ -181,14 +182,14 @@ </record> <menuitem id="menu_customer_commision_acct" - name="Customer Commision" + name="Customer Benefits" action="customer_commision_action" parent="account.menu_finance_entries" sequence="113" /> <menuitem id="menu_customer_commision_sales" - name="Customer Commision" + name="Customer Benefits" action="customer_commision_action" parent="sale.product_menu_catalog" sequence="101" diff --git a/indoteknik_custom/views/ir_sequence.xml b/indoteknik_custom/views/ir_sequence.xml index 97bf40bb..a0f5fc6b 100644 --- a/indoteknik_custom/views/ir_sequence.xml +++ b/indoteknik_custom/views/ir_sequence.xml @@ -131,7 +131,7 @@ <field name="number_increment">1</field> </record> - <record id="sequence_commision_customer" model="ir.sequence"> + <!-- <record id="sequence_commision_customer" model="ir.sequence"> <field name="name">Customer Commision</field> <field name="code">customer.commision</field> <field name="active">TRUE</field> @@ -139,6 +139,46 @@ <field name="padding">5</field> <field name="number_next">1</field> <field name="number_increment">1</field> + </record> --> + + <record id="sequence_commision_cashback" model="ir.sequence"> + <field name="name">Customer Commision Cashback</field> + <field name="code">customer.commision.cashback</field> + <field name="prefix">CB/%(year)s/</field> + <field name="padding">5</field> + <field name="number_next">1</field> + <field name="number_increment">1</field> + <field name="active">True</field> + </record> + + <record id="sequence_approval_payment_term" model="ir.sequence"> + <field name="name">Approval Payment Term</field> + <field name="code">approval.payment.term</field> + <field name="prefix">APP/%(year)s/</field> + <field name="padding">5</field> + <field name="number_next">1</field> + <field name="number_increment">1</field> + <field name="active">True</field> + </record> + + <record id="sequence_commision_fee" model="ir.sequence"> + <field name="name">Customer Commision Fee</field> + <field name="code">customer.commision.fee</field> + <field name="prefix">FE/%(year)s/</field> + <field name="padding">5</field> + <field name="number_next">1</field> + <field name="number_increment">1</field> + <field name="active">True</field> + </record> + + <record id="sequence_commision_rebate" model="ir.sequence"> + <field name="name">Customer Commision Rebate</field> + <field name="code">customer.commision.rebate</field> + <field name="prefix">RB/%(year)s/</field> + <field name="padding">5</field> + <field name="number_next">1</field> + <field name="number_increment">1</field> + <field name="active">True</field> </record> <record id="sequence_automatic_purchase" model="ir.sequence"> diff --git a/indoteknik_custom/views/mrp_production.xml b/indoteknik_custom/views/mrp_production.xml index 3de52a08..5057415f 100644 --- a/indoteknik_custom/views/mrp_production.xml +++ b/indoteknik_custom/views/mrp_production.xml @@ -11,7 +11,7 @@ <field name="bom_id" position="after"> <field name="desc"/> <field name="sale_order"/> - <field name="is_po"/> + <field name="is_po" readonly="1"/> </field> <xpath expr="//form/sheet/notebook/page/field[@name='move_raw_ids']/tree/field[@name='product_uom_qty']" position="before"> <field name="vendor_id"/> diff --git a/indoteknik_custom/views/purchase_order.xml b/indoteknik_custom/views/purchase_order.xml index 7b568d07..dae23eed 100755 --- a/indoteknik_custom/views/purchase_order.xml +++ b/indoteknik_custom/views/purchase_order.xml @@ -87,7 +87,8 @@ <field name="payment_term_id"/> <field name="total_cost_service" attrs="{'required': [('partner_id', 'in', [9688, 29712])]}"/> <field name="total_delivery_amt" attrs="{'required': [('partner_id', 'in', [9688, 29712])]}"/> - <field name="product_bom_id"/> + <field name="product_bom_id" attrs="{'invisible': [('product_bom_id', '=', None)]}"/> + <field name="manufacturing_id" attrs="{'invisible': [('product_bom_id', '=', None)]}"/> <!-- <field name="move_id" domain="[('move_type','=','entry')]" context="{'form_view_ref': 'account.view_move_form'}" options="{'no_create': True}"/> --> </field> <field name="amount_total" position="after"> @@ -332,6 +333,7 @@ <field name="delivery_amt" optional="hide"/> <field name="margin_deduct" optional="hide"/> <field name="hold_outgoing_so" optional="hide"/> + <field name="bu_pick" optional="hide"/> <field name="margin_so"/> </tree> </field> diff --git a/indoteknik_custom/views/res_partner.xml b/indoteknik_custom/views/res_partner.xml index 30b5ca36..ac4d0364 100644 --- a/indoteknik_custom/views/res_partner.xml +++ b/indoteknik_custom/views/res_partner.xml @@ -108,6 +108,13 @@ <xpath expr="//field[@name='property_supplier_payment_term_id']" position="attributes"> <attribute name="readonly">1</attribute> </xpath> + <xpath expr="//notebook/page[@name='accounting']" position="inside"> + <group string="Aging Info"> + <field name="avg_aging" readonly="1"/> + <field name="payment_difficulty" attrs="{'readonly': [('parent_id', '!=', False)]}" /> + <field name="payment_history_url" readonly="1" /> + </group> + </xpath> <notebook> <page string="Pengajuan Tempo"> <!-- Informasi Usaha Section --> @@ -181,6 +188,11 @@ <field name="dokumen_pengiriman_input"/> <field name="dokumen_invoice"/> </group> + <!-- <group string="Aging Info"> + <field name="avg_aging" readonly="1"/> + <field name="payment_difficulty" attrs="{'readonly': [('parent_id', '!=', False)]}" /> + <field name="payment_history_url" readonly="1" /> + </group> --> </group> <!-- Supplier Lines Section --> |
