from odoo import fields, models, api, _ from odoo.exceptions import AccessError, UserError, ValidationError from odoo.tools.misc import formatLang, get_lang import logging import warnings import random import string import requests import math import json from datetime import timedelta, date _logger = logging.getLogger(__name__) class SaleOrder(models.Model): _inherit = "sale.order" total_margin = fields.Float( 'Total Margin', compute='compute_total_margin', help="Total Margin in Sales Order Header") total_percent_margin = fields.Float( 'Total Percent Margin', compute='compute_total_margin', help="Total % Margin in Sales Order Header") approval_status = fields.Selection([ ('pengajuan1', 'Approval Manager'), ('pengajuan2', 'Approval Pimpinan'), ('approved', 'Approved'), ], string='Approval Status', readonly=True, copy=False, index=True, tracking=3) carrier_id = fields.Many2one('delivery.carrier', string='Shipping Method') have_visit_service = fields.Boolean(string='Have Visit Service', help='To compute is customer get visit service', compute='_compute_have_visit_service') delivery_amt = fields.Float('Delivery Amt') shipping_cost_covered = fields.Selection([ ('indoteknik', 'Indoteknik'), ('customer', 'Customer') ], string='Shipping Covered by', help='Siapa yang menanggung biaya ekspedisi?', copy=False) shipping_paid_by = fields.Selection([ ('indoteknik', 'Indoteknik'), ('customer', 'Customer') ], string='Shipping Paid by', help='Siapa yang talangin dulu Biaya ekspedisi-nya?', copy=False) sales_tax_id = fields.Many2one('account.tax', string='Tax', domain=['|', ('active', '=', False), ('active', '=', True)]) have_outstanding_invoice = fields.Boolean('Have Outstanding Invoice', compute='_have_outstanding_invoice') have_outstanding_picking = fields.Boolean('Have Outstanding Picking', compute='_have_outstanding_picking') have_outstanding_po = fields.Boolean('Have Outstanding PO', compute='_have_outstanding_po') purchase_ids = fields.Many2many('purchase.order', string='Purchases', compute='_get_purchases') real_shipping_id = fields.Many2one( 'res.partner', string='Real Delivery Address', readonly=True, required=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)], 'sale': [('readonly', False)]}, domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]", help="Dipakai untuk alamat tempel") fee_third_party = fields.Float('Fee Pihak Ketiga') so_status = fields.Selection([ ('terproses', 'Terproses'), ('sebagian', 'Sebagian Diproses'), ('menunggu', 'Menunggu Diproses'), ], copy=False) partner_purchase_order_name = fields.Char(string='Nama PO Customer', copy=False, help="Nama purchase order customer, diisi oleh customer melalui website.", tracking=3) partner_purchase_order_description = fields.Text(string='Keterangan PO Customer', copy=False, help="Keterangan purchase order customer, diisi oleh customer melalui website.", tracking=3) partner_purchase_order_file = fields.Binary(string='File PO Customer', copy=False, help="File purchase order customer, diisi oleh customer melalui website.") payment_status = fields.Selection([ ('pending', 'Pending'), ('capture', 'Capture'), ('settlement', 'Settlement'), ('deny', 'Deny'), ('cancel', 'Cancel'), ('expire', 'Expire'), ('failure', 'Failure'), ('refund', 'Refund'), ('chargeback', 'Chargeback'), ('partial_refund', 'Partial Refund'), ('partial_chargeback', 'Partial Chargeback'), ('authorize', 'Authorize'), ], tracking=True, string='Payment Status', help='Payment Gateway Status / Midtrans / Web, https://docs.midtrans.com/en/after-payment/status-cycle') date_doc_kirim = fields.Datetime(string='Tanggal Kirim di SJ', help="Tanggal Kirim di cetakan SJ yang terakhir, tidak berpengaruh ke Accounting") payment_type = fields.Char(string='Payment Type', help='Jenis pembayaran dengan Midtrans') gross_amount = fields.Float(string='Gross Amount', help='Jumlah pembayaran yang dilakukan dengan Midtrans') notification = fields.Char(string='Notification', help='Dapat membantu error dari approval') delivery_service_type = fields.Char(string='Delivery Service Type', help='data dari rajaongkir') grand_total = fields.Monetary(string='Grand Total', help='Amount total + amount delivery', compute='_compute_grand_total') payment_link_midtrans = fields.Char(string='Payment Link', help='Url payment yg digenerate oleh midtrans, harap diserahkan ke customer agar dapat dilakukan pembayaran secara mandiri') due_id = fields.Many2one('due.extension', string="Due Extension", readonly=True, tracking=True) customer_type = fields.Selection([ ('pkp', 'PKP'), ('nonpkp', 'Non PKP') ]) sppkp = fields.Char(string="SPPKP") npwp = fields.Char(string="NPWP") purchase_total = fields.Monetary(string='Purchase Total', compute='_compute_purchase_total') voucher_id = fields.Many2one(comodel_name='voucher', string='Voucher') amount_voucher_disc = fields.Float(string='Voucher Discount') def _compute_purchase_total(self): for order in self: total = 0 for line in order.order_line: total += line.vendor_subtotal order.purchase_total = total def generate_payment_link_midtrans_sales_order(self): # midtrans_url = 'https://app.sandbox.midtrans.com/snap/v1/transactions' # dev - sandbox # midtrans_auth = 'Basic U0ItTWlkLXNlcnZlci1uLVY3ZDJjMlpCMFNWRUQyOU95Q1dWWXA6' # dev - sandbox midtrans_url = 'https://app.midtrans.com/snap/v1/transactions' # production midtrans_auth = 'Basic TWlkLXNlcnZlci1SbGMxZ2gzWGpSVW5scl9JblZzTV9OTnU6' # production so_number = self.name so_number = so_number.replace('/', '-') so_grandtotal = math.floor(self.grand_total) headers = { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': midtrans_auth, } json_data = { 'transaction_details': { 'order_id': so_number, 'gross_amount': so_grandtotal, }, 'credit_card': { 'secure': True, }, } response = requests.post(midtrans_url, headers=headers, json=json_data).json() lookup_json = json.dumps(response, indent=4, sort_keys=True) redirect_url = json.loads(lookup_json)['redirect_url'] self.payment_link_midtrans = str(redirect_url) @api.model def _generate_so_access_token(self, limit=50): orders = self.search([('access_token', '=', False)], limit=limit) for order in orders: token_source = string.ascii_letters + string.digits order.access_token = ''.join(random.choice(token_source) for i in range(20)) def calculate_line_no(self): line_no = 0 for line in self.order_line: if line.product_id.type == 'product': line_no += 1 line.line_no = line_no # _logger.info('Calculate PO Line No %s' % line.id) def write(self, vals): res = super(SaleOrder, self).write(vals) if 'carrier_id' in vals: for picking in self.picking_ids: if picking.state == 'assigned': picking.carrier_id = self.carrier_id return res def calculate_so_status_beginning(self): so_state = ['sale'] sales = self.env['sale.order'].search([ ('state', 'in', so_state),# must add validation so_status ]) for sale in sales: sum_qty_ship = sum_qty_so = 0 have_outstanding_pick = False for pick in sale.picking_ids: if pick.state == 'draft' or pick.state == 'assigned' or pick.state == 'confirmed' or pick.state == 'waiting': have_outstanding_pick = True for so_line in sale.order_line: sum_qty_so += so_line.product_uom_qty sum_qty_ship += so_line.qty_delivered if have_outstanding_pick: if sum_qty_so > sum_qty_ship > 0: sale.so_status = 'sebagian' else: sale.so_status = 'menunggu' else: sale.so_status = 'terproses' _logger.info('Calculate SO Status %s' % sale.id) def _calculate_all_so_status(self, limit=500): so_state = ['sale'] sales = self.env['sale.order'].search([ ('state', 'in', so_state), # ('so_status', '!=', 'terproses'), ], order='id desc', limit=limit) for sale in sales: sum_qty_ship = sum_qty_so = 0 have_outstanding_pick = False for pick in sale.picking_ids: if pick.state == 'draft' or pick.state == 'assigned' or pick.state == 'confirmed' or pick.state == 'waiting': have_outstanding_pick = True for so_line in sale.order_line: sum_qty_so += so_line.product_uom_qty sum_qty_ship += so_line.qty_delivered if have_outstanding_pick: if sum_qty_so > sum_qty_ship > 0: sale.so_status = 'sebagian' else: sale.so_status = 'menunggu' else: sale.so_status = 'terproses' _logger.info('Calculate All SO Status %s' % sale.id) def calculate_so_status(self): so_state = ['sale'] sales = self.env['sale.order'].search([ ('state', 'in', so_state), ('so_status', '!=', 'terproses'), ]) for sale in sales: sum_qty_ship = sum_qty_so = 0 have_outstanding_pick = False for pick in sale.picking_ids: if pick.state == 'draft' or pick.state == 'assigned' or pick.state == 'confirmed' or pick.state == 'waiting': have_outstanding_pick = True for so_line in sale.order_line: sum_qty_so += so_line.product_uom_qty sum_qty_ship += so_line.qty_delivered if have_outstanding_pick: if sum_qty_so > sum_qty_ship > 0: sale.so_status = 'sebagian' else: sale.so_status = 'menunggu' else: sale.so_status = 'terproses' _logger.info('Calculate SO Status %s' % sale.id) @api.onchange('partner_shipping_id') def onchange_partner_shipping(self): self.real_shipping_id = self.partner_shipping_id @api.onchange('partner_id') def onchange_partner_contact(self): parent_id = self.partner_id.parent_id parent_id = parent_id if parent_id else self.partner_id self.npwp = parent_id.npwp self.sppkp = parent_id.sppkp self.customer_type = parent_id.customer_type def _get_purchases(self): po_state = ['done', 'draft', 'purchase'] for order in self: purchases = self.env['purchase.order'].search([ ('sale_order_id', '=', order.id), ('state', 'in', po_state) ]) order.purchase_ids = purchases def _have_outstanding_invoice(self): invoice_state = ['posted', 'draft'] for order in self: if not order.invoice_ids: order.have_outstanding_invoice = False for invoice in order.invoice_ids: if invoice.state in invoice_state: order.have_outstanding_invoice = True else: order.have_outstanding_invoice = False def _have_outstanding_picking(self): picking_state = ['done', 'confirmed', 'draft'] for order in self: if not order.picking_ids: order.have_outstanding_picking = False for picking in order.picking_ids: if picking in picking_state: order.have_outstanding_picking = True else: order.have_outstanding_picking = False def _have_outstanding_po(self): po_state = ['done', 'draft', 'purchase'] for order in self: if not order.purchase_ids: order.have_outstanding_po = False for purchase in order.purchase_ids: if purchase.state in po_state: order.have_outstanding_po = True else: order.have_outstanding_po = False def _compute_have_visit_service(self): limit = 20000000 self.have_visit_service = False if self.amount_total > limit: self.have_visit_service = True def check_due(self): """To show the due amount and warning stage""" for order in self: if order.partner_id.parent_id: partner_id = order.partner_id.parent_id else: partner_id = order.partner_id if partner_id and partner_id.due_amount > 0 \ and partner_id.active_limit \ and partner_id.enable_credit_limit: order.has_due = True else: order.has_due = False if partner_id and partner_id.active_limit\ and partner_id.enable_credit_limit: if order.outstanding_amount >= partner_id.warning_stage: if partner_id.warning_stage != 0: order.is_warning = True else: order.is_warning = False else: order.is_warning = False def sale_order_approve(self): # raise UserError("Bisa langsung Confirm") self.check_due() for order in self: if order.warehouse_id.id != 8: #GD Bandengan raise UserError('Gudang harus Bandengan') if order.state == 'cancel' or order.state == 'done' or order.state == 'sale': raise UserError("Status harus draft atau sent") if not order.partner_invoice_id.npwp: raise UserError("NPWP harus diisi di master data konsumen, jika non pkp dapat diisi 00.000.000.0-000.000") if order.partner_id.parent_id: if not order.partner_id.parent_id.property_payment_term_id: raise UserError("Payment Term pada Master Data Customer harus diisi") if not order.partner_id.parent_id.active_limit: raise UserError("Credit Limit pada Master Data Customer harus diisi") if order.payment_term_id != order.partner_id.parent_id.property_payment_term_id: raise UserError("Payment Term berbeda pada Master Data Customer") else: if not order.partner_id.property_payment_term_id: raise UserError("Payment Term pada Master Data Customer harus diisi") if not order.partner_id.active_limit: raise UserError("Credit Limit pada Master Data Customer harus diisi") if order.payment_term_id != order.partner_id.property_payment_term_id: raise UserError("Payment Term berbeda pada Master Data Customer") if not order.sales_tax_id: raise UserError("Tax di Header harus diisi") if not order.carrier_id: raise UserError("Shipping Method harus diisi") for line in order.order_line: if not line.product_id or line.product_id.type == 'service': continue # must add product can sell validation if not line.product_id.product_tmpl_id.sale_ok: raise UserError('Product %s belum bisa dijual, harap hubungi finance' % line.product_id.display_name) if line.product_id.id == 224484: raise UserError(_('Tidak bisa Confirm menggunakan Produk Sementara')) if not line.vendor_id or not line.purchase_price: raise UserError(_('Isi Vendor dan Harga Beli sebelum Request Approval')) if order.validate_partner_invoice_due(): return self._notification_has_unapprove_due() if order._notification_margin_leader(): order.approval_status = 'pengajuan2' return self._notification_has_margin_leader() elif order._notification_margin_manager(): order.approval_status = 'pengajuan1' return self._notification_has_margin_manager() else: raise UserError("Bisa langsung Confirm") def action_cancel(self): # TODO stephan prevent cancel if have invoice, do, and po if self._name != 'sale.order': return super(SaleOrder, self).action_cancel() if self.have_outstanding_invoice: raise UserError("Invoice harus di Cancel dahulu") elif self.have_outstanding_picking: raise UserError("DO harus di Cancel dahulu") # elif self.have_outstanding_po: # raise UserError("PO harus di Cancel dahulu") self.approval_status = False self.due_id = False return super(SaleOrder, self).action_cancel() def validate_partner_invoice_due(self): parent_id = self.partner_id.parent_id.id parent_id = parent_id if parent_id else self.partner_id.id if self.due_id and self.due_id.is_approve == False: raise UserError('Document Over Due Yang Anda Buat Belum Di Approve') query = [ ('partner_id', '=', parent_id), ('state', '=', 'posted'), ('move_type', '=', 'out_invoice'), ('amount_residual_signed', '>', 0) ] invoices = self.env['account.move'].search(query, order='invoice_date') if invoices: if not self.env.user.is_leader and not self.env.user.is_sales_manager: due_extension = self.env['due.extension'].create([{ 'partner_id': parent_id, 'day_extension': '3', 'order_id': self.id, }]) due_extension.generate_due_line() self.due_id = due_extension.id if len(self.due_id.due_line) > 0: return True else: due_extension.unlink() return False def _notification_margin_leader(self): if self.total_percent_margin <= 15 and not self.env.user.is_leader: return True else: return False def _notification_margin_manager(self): if self.total_percent_margin <= 22 and not self.env.user.is_leader and not self.env.user.is_sales_manager: return True else: return False def _notification_has_unapprove_due(self): return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': 'Notification', 'message': 'Ada Invoice Yang Sudah Over Due, Silahkan Memperbarui Over Due di Due Extension', 'next': {'type': 'ir.actions.act_window_close'}, } } def _notification_has_margin_leader(self): return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': 'Notification', 'message': 'SO Harus Di Approve Oleh Pimpinan', 'next': {'type': 'ir.actions.act_window_close'}, } } def _notification_has_margin_manager(self): return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': 'Notification', 'message': 'SO Harus Di Approve Oleh Sales Manager', 'next': {'type': 'ir.actions.act_window_close'}, } } def _set_sppkp_npwp_contact(self): parent_id = self.partner_id.parent_id parent_id = parent_id if parent_id else self.partner_id parent_id.customer_type = self.customer_type parent_id.npwp = self.npwp parent_id.sppkp = self.sppkp def action_confirm(self): for order in self: if order.warehouse_id.id != 8: #GD Bandengan raise UserError('Gudang harus Bandengan') if not order.sales_tax_id: raise UserError("Tax di Header harus diisi") if not order.carrier_id: raise UserError("Shipping Method harus diisi") # approval1 = approval2 = 0 for line in order.order_line: if not line.product_id or line.product_id.type == 'service': continue # must add product can sell validation if not line.product_id.product_tmpl_id.sale_ok: raise UserError('Product %s belum bisa dijual, harap hubungi finance' % line.product_id.display_name) if line.product_id.id == 224484: raise UserError(_('Tidak bisa Confirm menggunakan Produk Sementara')) if not line.vendor_id or not line.purchase_price or not line.purchase_tax_id: raise UserError(_('Isi Vendor, Harga Beli, dan Tax sebelum Request Approval')) if order.validate_partner_invoice_due(): return self._notification_has_unapprove_due() if order._notification_margin_leader(): return self._notification_has_margin_leader() elif order._notification_margin_manager(): return self._notification_has_margin_manager() else: order.approval_status = 'approved' order._set_sppkp_npwp_contact() order.calculate_line_no() res = super(SaleOrder, self).action_confirm() return res def _have_outstanding_invoices(self): partner_id = self.partner_id if self.partner_id.parent_id: partner_id = self.partner_id partners = [] partners += partner_id.child_ids partners.append(partner_id) for partner in partners: query = [ ('partner_id', '=', partner.id), ('state', '=', 'posted'), ('move_type', '=', 'out_invoice'), ('amount_residual_signed', '>', 0) ] invoices = self.env['account.move'].search(query) for invoice in invoices: if invoice.invoice_day_to_due < 0: self.notification = 'Ada %s yang sudah jatuh tempo dan belum lunas' % invoice.name # self.env.cr.commit() return True def compute_total_margin(self): for order in self: total_margin = total_percent_margin = 0 for line in order.order_line: if not line.product_id: order.total_margin = 0 order.total_percent_margin = 0 continue total_margin += line.item_margin # sales_price = line.price_reduce_taxexcl * line.product_uom_qty # if line.order_id.shipping_cost_covered == 'indoteknik': # sales_price -= line.delivery_amt_line # if line.order_id.fee_third_party > 0: # sales_price -= line.fee_third_party_line # sum_sales_price += sales_price order.total_margin = total_margin if order.amount_untaxed > 0: total_percent_margin = round((total_margin / order.amount_untaxed), 2) * 100 order.total_percent_margin = total_percent_margin def compute_count_line_product(self): for order in self: count = 0 for line in order.order_line: if line.product_id.type == 'product': count += 1 if count == 0: order.count_line_product = 1 else: order.count_line_product = count @api.onchange('sales_tax_id') def onchange_sales_tax_id(self): for line in self.order_line: line.product_id_change() def _compute_grand_total(self): for order in self: if order.shipping_cost_covered == 'customer': order.grand_total = order.delivery_amt + order.amount_total else: order.grand_total = order.amount_total class SaleOrderLine(models.Model): _inherit = 'sale.order.line' item_margin = fields.Float( 'Margin', compute='compute_item_margin', help="Total Margin in Sales Order Header") item_percent_margin = fields.Float( '%Margin', compute='compute_item_margin', help="Total % Margin in Sales Order Header") vendor_id = fields.Many2one( 'res.partner', string='Vendor', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, change_default=True, index=True, tracking=1, domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]",) purchase_price = fields.Float('Purchase', required=True, digits='Product Price', default=0.0) purchase_tax_id = fields.Many2one('account.tax', string='Tax', domain=['|', ('active', '=', False), ('active', '=', True)]) delivery_amt_line = fields.Float('DeliveryAmtLine', compute='compute_delivery_amt_line') fee_third_party_line = fields.Float('FeeThirdPartyLine', compute='compute_fee_third_party_line', default=0) line_no = fields.Integer('No', default=0, copy=False) note_procurement = fields.Char(string='Note', help="Harap diisi jika ada keterangan tambahan dari Procurement, agar dapat dimonitoring") vendor_subtotal = fields.Float(string='Vendor Subtotal', compute="_compute_vendor_subtotal") amount_voucher_disc = fields.Float(string='Voucher Discount') program_line_id = fields.Many2one('promotion.program.line', 'Program Line') def _compute_vendor_subtotal(self): for line in self: if line.purchase_price > 0 and line.product_uom_qty > 0: # product = line.product_id # if product: # vendor_price = line.purchase_price # if line.purchase_tax_id.price_include: # vendor_price = line.purchase_price # else: # vendor_price = line.purchase_price + (line.purchase_price*11/100) subtotal = line.purchase_price * line.product_uom_qty line.vendor_subtotal = subtotal else: line.vendor_subtotal = 0 def compute_item_margin(self): for line in self: if not line.product_id or line.product_id.type == 'service' \ or line.price_unit <= 0 or line.product_uom_qty <= 0 \ or not line.vendor_id: line.item_margin = 0 line.item_percent_margin = 0 continue # calculate margin without tax sales_price = line.price_reduce_taxexcl * line.product_uom_qty # minus with delivery if covered by indoteknik if line.order_id.shipping_cost_covered == 'indoteknik': sales_price -= line.delivery_amt_line if line.order_id.fee_third_party > 0: sales_price -= line.fee_third_party_line purchase_price = line.purchase_price if line.purchase_tax_id.price_include: purchase_price = line.purchase_price / 1.11 purchase_price = purchase_price * line.product_uom_qty margin_per_item = sales_price - purchase_price line.item_margin = margin_per_item if sales_price > 0: line.item_percent_margin = round((margin_per_item / sales_price), 2) * 100 else: line.item_percent_margin = 0 @api.onchange('vendor_id') def onchange_vendor_id(self): if not self.product_id or self.product_id.type == 'service': return elif self.product_id.categ_id.id == 34: # finish good / manufacturing only # bom = self.env['mrp.bom'].search( # [('product_tmpl_id', '=', self.product_id.product_tmpl_id.id)] # , limit=1) # cost = 0 # for line in bom.bom_line_ids: # purchase_price = self.env['purchase.pricelist'].search( # [('vendor_id', '=', self.vendor_id.id), ('product_id', '=', line.product_id.id)], limit=1) # cost += purchase_price.product_price cost = self.product_id.standard_price self.purchase_price = cost else: purchase_price = self.env['purchase.pricelist'].search( [('vendor_id', '=', self.vendor_id.id), ('product_id', '=', self.product_id.id)], limit=1) self.purchase_price = purchase_price.product_price self.purchase_tax_id = 22 @api.onchange('product_id') def product_id_change(self): super(SaleOrderLine, self).product_id_change() for line in self: if line.product_id and line.product_id.type == 'product': purchase_price = self.env['purchase.pricelist'].search( [('product_id', '=', self.product_id.id)], limit=1, order='product_price ASC') line.vendor_id = purchase_price.vendor_id line.tax_id = line.order_id.sales_tax_id line.purchase_price = purchase_price.product_price def compute_delivery_amt_line(self): for line in self: try: contribution = round((line.price_total / line.order_id.amount_total), 2) except: contribution = 0 delivery_amt = line.order_id.delivery_amt line.delivery_amt_line = delivery_amt * contribution def compute_fee_third_party_line(self): for line in self: try: contribution = round((line.price_total / line.order_id.amount_total), 2) except: contribution = 0 fee = line.order_id.fee_third_party line.fee_third_party_line = fee * contribution @api.onchange('product_id', 'price_unit', 'product_uom', 'product_uom_qty', 'tax_id') def _onchange_discount(self): if not (self.product_id and self.product_uom and self.order_id.partner_id and self.order_id.pricelist_id and self.order_id.pricelist_id.discount_policy == 'without_discount' and self.env.user.has_group('product.group_discount_per_so_line')): return self.discount = 0.0 product = self.product_id.with_context( lang=self.order_id.partner_id.lang, partner=self.order_id.partner_id, quantity=self.product_uom_qty, date=self.order_id.date_order, pricelist=self.order_id.pricelist_id.id, uom=self.product_uom.id, fiscal_position=self.env.context.get('fiscal_position') ) product_context = dict(self.env.context, partner_id=self.order_id.partner_id.id, date=self.order_id.date_order, uom=self.product_uom.id) price, rule_id = self.order_id.pricelist_id.with_context(product_context).get_product_price_rule( self.product_id, self.product_uom_qty or 1.0, self.order_id.partner_id) new_list_price, currency = self.with_context(product_context)._get_real_price_currency(product, rule_id, self.product_uom_qty, self.product_uom, self.order_id.pricelist_id.id) new_list_price = product.web_price if new_list_price != 0: if self.order_id.pricelist_id.currency_id != currency: # we need new_list_price in the same currency as price, which is in the SO's pricelist's currency new_list_price = currency._convert( new_list_price, self.order_id.pricelist_id.currency_id, self.order_id.company_id or self.env.company, self.order_id.date_order or fields.Date.today()) discount = (new_list_price - price) / new_list_price * 100 if (discount > 0 and new_list_price > 0) or (discount < 0 and new_list_price < 0): self.discount = discount def _get_display_price(self, product): # TO DO: move me in master/saas-16 on sale.order # awa: don't know if it's still the case since we need the "product_no_variant_attribute_value_ids" field now # to be able to compute the full price # it is possible that a no_variant attribute is still in a variant if # the type of the attribute has been changed after creation. no_variant_attributes_price_extra = [ ptav.price_extra for ptav in self.product_no_variant_attribute_value_ids.filtered( lambda ptav: ptav.price_extra and ptav not in product.product_template_attribute_value_ids ) ] if no_variant_attributes_price_extra: product = product.with_context( no_variant_attributes_price_extra=tuple(no_variant_attributes_price_extra) ) if self.order_id.pricelist_id.discount_policy == 'with_discount': return product.with_context(pricelist=self.order_id.pricelist_id.id, uom=self.product_uom.id).price product_context = dict(self.env.context, partner_id=self.order_id.partner_id.id, date=self.order_id.date_order, uom=self.product_uom.id) final_price, rule_id = self.order_id.pricelist_id.with_context(product_context).get_product_price_rule(product or self.product_id, self.product_uom_qty or 1.0, self.order_id.partner_id) base_price, currency = self.with_context(product_context)._get_real_price_currency(product, rule_id, self.product_uom_qty, self.product_uom, self.order_id.pricelist_id.id) base_price = product.web_price if currency != self.order_id.pricelist_id.currency_id: base_price = currency._convert( base_price, self.order_id.pricelist_id.currency_id, self.order_id.company_id or self.env.company, self.order_id.date_order or fields.Date.today()) # negative discounts (= surcharge) are included in the display price return max(base_price, final_price)