diff options
| author | IT Fixcomart <it@fixcomart.co.id> | 2023-08-16 04:51:39 +0000 |
|---|---|---|
| committer | IT Fixcomart <it@fixcomart.co.id> | 2023-08-16 04:51:39 +0000 |
| commit | b0dd1bde531a90a867448c03ec08b72cc70f0053 (patch) | |
| tree | 0ec22360367159e8b928b030351d6a8b53f3ada4 | |
| parent | 9f8664c33429e41c6133ba2b325662245ffbc92d (diff) | |
| parent | a73c0825a264fc002e7496b07f238f79efd93086 (diff) | |
Merged in refactor (pull request #99)
Refactor
| -rw-r--r-- | indoteknik_api/controllers/controller.py | 22 | ||||
| -rwxr-xr-x | indoteknik_custom/models/__init__.py | 1 | ||||
| -rw-r--r-- | indoteknik_custom/models/account_financial_report.py | 164 | ||||
| -rw-r--r-- | indoteknik_custom/models/account_report_financial.py | 8 | ||||
| -rwxr-xr-x | indoteknik_custom/models/crm_lead.py | 21 | ||||
| -rw-r--r-- | indoteknik_custom/models/performance_test.py | 20 | ||||
| -rw-r--r-- | indoteknik_custom/models/product_pricelist.py | 18 | ||||
| -rwxr-xr-x | indoteknik_custom/models/purchase_order.py | 2 | ||||
| -rw-r--r-- | indoteknik_custom/models/res_partner.py | 7 | ||||
| -rwxr-xr-x | indoteknik_custom/models/sale_order.py | 600 | ||||
| -rw-r--r-- | indoteknik_custom/models/sale_order_line.py | 182 | ||||
| -rw-r--r-- | indoteknik_custom/views/account_financial_report_view.xml | 50 | ||||
| -rwxr-xr-x | indoteknik_custom/views/crm_lead.xml | 3 | ||||
| -rw-r--r-- | indoteknik_custom/views/mail_template_po.xml | 8 | ||||
| -rw-r--r-- | indoteknik_custom/views/product_pricelist.xml | 8 |
15 files changed, 535 insertions, 579 deletions
diff --git a/indoteknik_api/controllers/controller.py b/indoteknik_api/controllers/controller.py index 1e9f01ee..d40ccb5a 100644 --- a/indoteknik_api/controllers/controller.py +++ b/indoteknik_api/controllers/controller.py @@ -42,19 +42,10 @@ class Controller(http.Controller): request.session.authenticate(db, username, password) return True except: - try: - authorization = wsgienv['HTTP_AUTHORIZATION'] - except: - authorization = None - request.session.authenticate(config.get('db_name'), 'it@fixcomart.co.id', 'Fixcomart378') - token = request.env['ir.config_parameter'].sudo().get_param('rest_api_token') or '' - result = False - if authorization == token: - result = True + if not request.env.uid: + request.session.authenticate(config.get('db_name'), 'it@fixcomart.co.id', 'Fixcomart378') user_token = self.verify_user_token() - if user_token: - result = user_token - return result + return user_token def user_pricelist(self): user_token = self.authenticate() @@ -147,13 +138,10 @@ class Controller(http.Controller): user = request.env['res.users'].browse([ user_token['id'] ]) if not user: return False - data = { + return { 'user_id': user.id, - 'partner_id': None + 'partner_id': user.partner_id.id or None } - if user.partner_id: - data['partner_id'] = user.partner_id.id - return data except: return False diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index 843beeae..5de392ca 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -27,6 +27,7 @@ from . import res_partner_company_type from . import res_partner from . import res_users from . import sale_order +from . import sale_order_line from . import sale_monitoring_detail from . import sale_monitoring from . import sales_outstanding diff --git a/indoteknik_custom/models/account_financial_report.py b/indoteknik_custom/models/account_financial_report.py index 1bf6816a..6147a3f3 100644 --- a/indoteknik_custom/models/account_financial_report.py +++ b/indoteknik_custom/models/account_financial_report.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, timedelta import json import datetime import io @@ -12,6 +12,166 @@ except ImportError: class AccountingReport(models.TransientModel): _inherit = "accounting.report.xlsx" + + by_month = fields.Boolean(_("Group By Month")) + + def get_xlsx_report(self, options, response): + obj = self.search([('id', '=', options['form']['id'])]) + if not obj.by_month: + res = super(AccountingReport, self).get_xlsx_report(options, response) + return res + + output = io.BytesIO() + workbook = xlsxwriter.Workbook(output, {'in_memory': True}) + journal_entries = self.env['account.move'].search([('move_type', '=', 'entry')], order='date asc', limit=1) + first_journal_date = journal_entries.date + data = {} + data['form'] = obj.read([])[0] + + date_from = data['form']['date_from'] + date_to = data['form']['date_to'] + + obj = self.search([('id', '=', options['form']['id'])]) + data['form'] = obj.read([])[0] + comp_dic = {} + env_obj = obj.env['report.account.report_financial'] + data['form']['used_context'] = {} + data['form']['used_context']['journal_ids'] = data['form']['journal_ids'] + data['form']['used_context']['state'] = 'posted' + data['form']['used_context']['strict_range'] = True + + sheet = workbook.add_worksheet() + format1 = workbook.add_format({'font_size': 16, 'align': 'center', 'bg_color': '#D3D3D3', 'bold': True}) + format1.set_font_color('#000080') + format1.set_font_name('Times New Roman') + format2 = workbook.add_format({'font_size': 12, 'bold': True, 'bg_color': '#D3D3D3'}) + format3 = workbook.add_format({'font_size': 10, 'bold': True}) + format4 = workbook.add_format({'font_size': 10}) + format6 = workbook.add_format({'font_size': 10, 'bold': True}) + format7 = workbook.add_format({'font_size': 10}) + format8 = workbook.add_format({'font_size': 12, 'bold': True, 'bg_color': '#D3D3D3'}) + format1.set_align('center') + format1.set_align('vcenter') + format2.set_align('center') + format3.set_align('center') + format4.set_align('center') + format6.set_align('right') + format8.set_align('center') + sheet.set_column('E:E', 10, format4) + sheet.set_column('H:H', 10, format4) + sheet.set_column('I:I', 10, format4) + sheet.set_column('J:J', 10, format4) + + date_ranges = self.generate_date_ranges(date_from, date_to) + + col_number = 0 + balance_col_number = 4 + + for date_range in date_ranges: + row_number = 9 + data['form']['used_context']['date_to'] = date_range['date_to'] + data['form']['used_context']['date_from'] = first_journal_date + comp_dic['state'] = 'posted' + comp_dic['journal_ids'] = data['form']['journal_ids'] + data['form']['comparison_context'] = comp_dic + data['account_report_id'] = data['form']['account_report_id'] + accounting_data = env_obj.get_account_lines(data.get('form')) + currency = self.env.user.company_id.currency_id.symbol + format7.set_num_format('0.00 ' + currency) + format6.set_num_format('0.00 ' + currency) + report_date = datetime.datetime.now().strftime("%Y-%m-%d") + sheet.merge_range('A1:B1', _("Report Date"), format6) + sheet.merge_range('C1:D1', report_date, format7) + if obj.account_report_id.name: + sheet.merge_range(3, 0, 4, 3 + len(date_ranges), obj.account_report_id.name, format1) + sheet.merge_range('C3:G3', _("PT. Indoteknik Dotcom Gemilang"), format4) + if obj.target_move == 'all': + target_moves = 'All entries' + else: + target_moves = 'All posted entries' + sheet.merge_range('A7:B7', _("Target Moves:"), format6) + sheet.write('C7', target_moves, format7) + if obj.date_from: + sheet.write(row_number, col_number, _("Date From:"), format6) + sheet.write(row_number, col_number + 1, str(obj.date_from), format7) + if obj.date_to: + sheet.write(row_number, col_number + 3, _("Date To:"), format6) + sheet.write(row_number, balance_col_number, str(date_range['date_to']), format7) + + row_number += 1 + if obj.debit_credit == 1: + sheet.merge_range('A9:G9', _("Name"), format8) + sheet.write('H9', _("Debit"), format2) + sheet.write('I9', _("Credit"), format2) + sheet.write('J9', _("Balance"), format2) + for values in accounting_data: + if not self.env.user.company_id.parent_id: + if values['level'] != 0: + pass + else: + if values['level'] != 0: + if values['level'] == 1: + sheet.write(row_number, col_number, values['name'], format6) + sheet.write(row_number, col_number + 7, values['debit'], format6) + sheet.write(row_number, col_number + 8, values['credit'], format6) + sheet.write(row_number, balance_col_number, values['balance'], format6) + row_number += 1 + elif not values['account_type'] == 'sum': + sheet.write(row_number, col_number, values['name'], format7) + sheet.write(row_number, col_number + 7, values['debit'], format7) + sheet.write(row_number, col_number + 8, values['credit'], format7) + sheet.write(row_number, balance_col_number, values['balance'], format7) + row_number += 1 + + if not obj.enable_filter and not obj.debit_credit: + sheet.write('A9', _("Name"), format8) + sheet.merge_range(8, 1, 8, len(date_ranges) + 3, _("Balance"), format2) + for values in accounting_data: + if values['level'] != 0: + if values['level'] == 1: + # assets and liabilities + sheet.write(row_number, col_number, values['name'], format6) + sheet.write(row_number, balance_col_number, values['balance'], format6) + row_number += 1 + elif not values['account_type'] == 'sum': + sheet.write(row_number, col_number, values['name'], format7) + sheet.write(row_number, balance_col_number, values['balance'], format7) + row_number += 1 + if obj.enable_filter and not obj.debit_credit: + sheet.merge_range('A9:H9', _("Name"), format8) + sheet.write('I9', _("Balance"), format2) + sheet.write('J9', data['form']['label_filter'], format2) + for values in accounting_data: + if values['level'] != 0: + if values['level'] == 1: + sheet.write(row_number, col_number, values['name'], format6) + sheet.write(row_number, balance_col_number, values['balance'], format6) + sheet.write(row_number, col_number + 9, values['balance_cmp'], format6) + row_number += 1 + elif not values['account_type'] == 'sum': + sheet.write(row_number, col_number, values['name'], format7) + sheet.write(row_number, balance_col_number, values['balance'], format7) + sheet.write(row_number, col_number + 9, values['balance_cmp'], format7) + row_number += 1 + balance_col_number += 1 + workbook.close() + output.seek(0) + response.stream.write(output.read()) + output.close() + def generate_date_ranges(self, date_from, date_to): + current_date = date_from + date_ranges = [] + + while current_date <= date_to: + next_month = current_date.replace(day=1) + timedelta(days=32) + end_of_month = min(next_month - timedelta(days=next_month.day), date_to) + + date_ranges.append({ + "date_from": current_date, + "date_to": end_of_month + }) + + current_date = end_of_month + timedelta(days=1) -
\ No newline at end of file + return date_ranges diff --git a/indoteknik_custom/models/account_report_financial.py b/indoteknik_custom/models/account_report_financial.py index c11fa25b..c1958842 100644 --- a/indoteknik_custom/models/account_report_financial.py +++ b/indoteknik_custom/models/account_report_financial.py @@ -38,10 +38,10 @@ class ReportFinancial(models.AbstractModel): def _compute_report_balance(self, reports): '''returns a dictionary with key=the ID of a record and value=the credit, debit and balance amount computed for this record. If the record is of type : - 'accounts' : it's the sum of the linked accounts - 'account_type' : it's the sum of leaf accoutns with such an account_type - 'account_report' : it's the amount of the related report - 'sum' : it's the sum of the children of this record (aka a 'view' record)''' + 'accounts': it's the sum of the linked accounts + 'account_type': it's the sum of leaf accoutns with such an account_type + 'account_report': it's the amount of the related report + 'sum': it's the sum of the children of this record (aka a 'view' record)''' res = {} fields = ['credit', 'debit', 'balance'] for report in reports: diff --git a/indoteknik_custom/models/crm_lead.py b/indoteknik_custom/models/crm_lead.py index e5d9953b..0c38ae56 100755 --- a/indoteknik_custom/models/crm_lead.py +++ b/indoteknik_custom/models/crm_lead.py @@ -1,6 +1,7 @@ from odoo import fields, models, api import logging import random +from odoo.exceptions import AccessError, UserError, ValidationError _logger = logging.getLogger(__name__) @@ -21,7 +22,27 @@ class CrmLead(models.Model): operator_email = fields.Char('Operator Email', help='Operator yang membalas') operator_name = fields.Char('Operator Name', help='Operator yang membalas') order_id = fields.Many2one('sale.order', string='Sales Order', help='Link ke sales order id') + reason = fields.Selection([ + ('new_so', 'Diganti SO Baru'), + ('out_stock', 'Stock Tidak Tersedia'), + ('wrong_price', 'Salah Memberikan Informasi Harga'), + ('discontinue', 'Barang Discontinue'), + ('change_of_mind', 'Konsumen Berubah Pikiran'), + ('problematic_items', 'Barang Yang Dibeli Bermasalah'), + ('no_tempo', 'Konsumen Tidak Mendapatkan Tempo'), + ('payment_issues', 'Konsumen Memiliki Masalah Pembayaran'), + ('disagree_shipping', 'Tidak Sepakat Dengan Biaya Kirim'), + ('payment_not_received', 'Pembayaran Belum Diterima'), + ('delivery_time', 'Tidak Sanggup Memenuhi Delivery Time'), + ('fraud_indication', 'Indikasi Penipuan'), + ], string='Reason Mark Lost', tracking=True) + def action_set_lost(self): + result = super(CrmLead, self).action_set_lost() + if not self.reason: + raise UserError('Jika ingin Mark as Lost, Isi Reason nya terlebih dahulu') + return result + @api.onchange('user_id') def _change_salesperson_so(self): if self.order_id: diff --git a/indoteknik_custom/models/performance_test.py b/indoteknik_custom/models/performance_test.py new file mode 100644 index 00000000..1782eb9d --- /dev/null +++ b/indoteknik_custom/models/performance_test.py @@ -0,0 +1,20 @@ +import time, logging + +_logger = logging.getLogger(__name__) + +def performance_test(num_tests): + def decorator(func): + def wrapper(*args, **kwargs): + total_time = 0 + for _ in range(num_tests): + start_time = time.time() + result = func(*args, **kwargs) + end_time = time.time() + total_time += end_time - start_time + + average_time = total_time / num_tests + _logger.info(f"Average execution time over {num_tests} tests: {average_time:.6f} seconds") + return result + + return wrapper + return decorator diff --git a/indoteknik_custom/models/product_pricelist.py b/indoteknik_custom/models/product_pricelist.py index 95e63cf0..2053accc 100644 --- a/indoteknik_custom/models/product_pricelist.py +++ b/indoteknik_custom/models/product_pricelist.py @@ -24,16 +24,22 @@ class ProductPricelist(models.Model): remaining_time = (self.end_date - datetime.now()).total_seconds() remaining_time = round(remaining_time) return max(remaining_time, 0) - + + def update_product_solr_flag(self): + for item in self.item_ids: + item.product_id.product_tmpl_id.solr_flag = 2 + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { 'title': 'Notification', 'message': f'{len(self.item_ids)} produk akan diupdate ke SOLR' } + } class ProductPricelistItem(models.Model): _inherit = 'product.pricelist.item' manufacture_id = fields.Many2one('x_manufactures', string='Manufacture') - @api.onchange('fixed_price','price_discount') - def update_solr_flag(self): + @api.constrains('fixed_price','price_discount') + def update_product_solr_flag(self): for item in self: - if item.product_id.product_tmpl_id.solr_flag == 1: - item.product_id.product_tmpl_id.solr_flag = 2 -
\ No newline at end of file + item.product_id.product_tmpl_id.solr_flag = 2
\ No newline at end of file diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index d73f9037..9e851ef9 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -196,7 +196,7 @@ class PurchaseOrder(models.Model): for line in self.order_line: if not line.product_id.purchase_ok: raise UserError("Terdapat barang yang tidak bisa diproses") - if line.price_vendor != 0 and line.price_unit != line.price_vendor: + if line.price_unit != line.price_vendor: send_email = True break diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index 1695639d..bc2a24b1 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -1,4 +1,4 @@ -from odoo import models, fields +from odoo import models, fields, api from odoo.exceptions import UserError, ValidationError class GroupPartner(models.Model): @@ -28,6 +28,11 @@ class ResPartner(models.Model): partner_child_ids += [partner.parent_id.id] return partner_child_ids + @api.constrains('kota_id') + def update_product_solr_flag(self): + for partner in self: + partner.city = partner.kota_id.name + def unlink(self): if self._name == 'res.partner': raise UserError('Maaf anda tidak bisa delete contact') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 25d28bbb..036fba1b 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -1,34 +1,21 @@ 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 +from odoo.exceptions import UserError +import logging, random, string, requests, math, json, re _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") + 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_percent_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') + have_visit_service = fields.Boolean(string='Have Visit Service', compute='_have_visit_service', help='To compute is customer get visit service') delivery_amt = fields.Float('Delivery Amt') shipping_cost_covered = fields.Selection([ ('indoteknik', 'Indoteknik'), @@ -38,8 +25,7 @@ class SaleOrder(models.Model): ('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)]) + 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') @@ -140,7 +126,6 @@ class SaleOrder(models.Model): 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) @@ -152,84 +137,25 @@ class SaleOrder(models.Model): 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([ + sales = self.search([ ('state', 'in', so_state), ('so_status', '!=', 'terproses'), ]) for sale in sales: - sum_qty_ship = sum_qty_so = 0 - have_outstanding_pick = False + picking_states = {'draft', 'assigned', 'confirmed', 'waiting'} + have_outstanding_pick = any(x.state in picking_states for x in sale.picking_ids) - 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 + sum_qty_so = sum(so_line.product_uom_qty for so_line in sale.order_line) + sum_qty_ship = sum(so_line.qty_delivered for so_line in sale.order_line) - 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: + if not have_outstanding_pick: sale.so_status = 'terproses' + elif sum_qty_so > sum_qty_ship > 0: + sale.so_status = 'sebagian' + else: + sale.so_status = 'menunggu' _logger.info('Calculate SO Status %s' % sale.id) @api.onchange('partner_shipping_id') @@ -267,121 +193,93 @@ class SaleOrder(models.Model): 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 + order.have_outstanding_invoice = any(inv.state in invoice_state for inv in order.invoice_ids) 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 + order.have_outstanding_picking = any(pick.state in picking_state for pick in order.picking_ids) 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 + order.have_outstanding_po = any(po.state in po_state for po in order.purchase_ids) - def _compute_have_visit_service(self): - limit = 20000000 - self.have_visit_service = False - if self.amount_total > limit: - self.have_visit_service = True + def _have_visit_service(self): + minimum_amount = 20000000 + for order in self: + order.have_visit_service = self.amount_total > minimum_amount 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 + partner = order.partner_id.parent_id or order.partner_id + if partner and partner.active_limit and partner.enable_credit_limit: + order.has_due = partner.due_amount > 0 + if order.outstanding_amount >= partner.warning_stage and partner.warning_stage != 0: + order.is_warning = 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 _validate_order(self): + if self.warehouse_id.id != 8: #GD Bandengan + raise UserError('Gudang harus Bandengan') + if self.state not in ['draft', 'sent']: + raise UserError("Status harus draft atau sent") + if not self._validate_npwp(): + raise UserError("Isi NPWP Dengan Benar!") + + def _validate_npwp(self): + pattern = r'^\d{2}\.\d{3}\.\d{3}\.\d{1}-\d{3}\.\d{3}$' + return re.match(pattern, self.npwp) is not None + def sale_order_approve(self): 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 '-' not in order.npwp or '.' not in order.npwp: - raise UserError("Isi NPWP Dengan Benar!") + order._validate_order() + order.order_line.validate_line() + + partner = order.partner_id.parent_id or order.partner_id + if not partner.property_payment_term_id: + raise UserError("Payment Term pada Master Data Customer harus diisi") + if not partner.active_limit: + raise UserError("Credit Limit pada Master Data Customer harus diisi") + if order.payment_term_id != partner.property_payment_term_id: + raise UserError("Payment Term berbeda pada Master Data Customer") - 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 line.product_id.id == 385544: - raise UserError('Produk Sementara Tidak Bisa Di Confirm atau Ask Approval') - 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() + return self._create_notification_action('Notification', 'Terdapat invoice yang telah melewati batas waktu, mohon perbarui pada dokumen Due Extension') - if order._notification_margin_leader(): + if order._requires_approval_margin_leader(): order.approval_status = 'pengajuan2' - return self._notification_has_margin_leader() - elif order._notification_margin_manager(): + return self._create_approval_notification('Pimpinan') + elif order._requires_approval_margin_manager(): order.approval_status = 'pengajuan1' - return self._notification_has_margin_manager() - else: - raise UserError("Bisa langsung Confirm") + return self._create_approval_notification('Sales Manager') + + raise UserError("Bisa langsung Confirm") + + def action_confirm(self): + for order in self: + order._validate_order() + order.order_line.validate_line() + + if order.validate_partner_invoice_due(): + return self._create_notification_action('Notification', 'Terdapat invoice yang telah melewati batas waktu, mohon perbarui pada dokumen Due Extension') + + if order._requires_approval_margin_leader(): + return self._create_approval_notification('Pimpinan') + elif order._requires_approval_margin_manager(): + return self._create_approval_notification('Sales Manager') + + order.approval_status = 'approved' + order._set_sppkp_npwp_contact() + order.calculate_line_no() + + res = super(SaleOrder, self).action_confirm() + return res def action_cancel(self): # TODO stephan prevent cancel if have invoice, do, and po @@ -429,148 +327,41 @@ class SaleOrder(models.Model): 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 _requires_approval_margin_leader(self): + return self.total_percent_margin <= 15 and not self.env.user.is_leader - 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 _requires_approval_margin_manager(self): + return self.total_percent_margin <= 22 and not self.env.user.is_leader and not self.env.user.is_sales_manager - 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): + def _create_approval_notification(self, approval_role): + title = 'Warning' + message = f'SO butuh approval {approval_role}' + return self._create_notification_action(title, message) + + def _create_notification_action(self, title, message): 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'}, - } + 'params': { 'title': title, 'message': message } } def _set_sppkp_npwp_contact(self): - parent_id = self.partner_id.parent_id - parent_id = parent_id if parent_id else self.partner_id + partner = self.partner_id.parent_id or self.partner_id - parent_id.customer_type = self.customer_type - parent_id.npwp = self.npwp - parent_id.sppkp = self.sppkp + partner.customer_type = self.customer_type + partner.npwp = self.npwp + partner.sppkp = self.sppkp - def action_confirm(self): + def _compute_total_margin(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") - if '-' not in order.npwp or '.' not in order.npwp: - raise UserError("Isi NPWP Dengan Benar!") - - 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 + total_margin = sum(line.item_margin for line in order.order_line if line.product_id) 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): + + def _compute_total_percent_margin(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 + if order.amount_untaxed == 0: + continue + order.total_percent_margin = round((order.total_margin / order.amount_untaxed) * 100, 2) @api.onchange('sales_tax_id') def onchange_sales_tax_id(self): @@ -584,196 +375,3 @@ class SaleOrder(models.Model): 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) diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py new file mode 100644 index 00000000..4e1209cb --- /dev/null +++ b/indoteknik_custom/models/sale_order_line.py @@ -0,0 +1,182 @@ +from odoo import fields, models, api, _ +from odoo.exceptions import UserError + + +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, + change_default=True, index=True, tracking=1, + states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, + 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: + line.vendor_subtotal = line.purchase_price * line.product_uom_qty + 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 + 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) + + def validate_line(self): + for line in self: + if line.product_id.id in [385544, 224484]: + raise UserError('Produk Sementara Tidak Bisa Di Confirm atau Ask Approval') + if not line.product_id or line.product_id.type == 'service': + continue + 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 not line.vendor_id or not line.purchase_price: + raise UserError(_('Isi Vendor dan Harga Beli sebelum Request Approval'))
\ No newline at end of file diff --git a/indoteknik_custom/views/account_financial_report_view.xml b/indoteknik_custom/views/account_financial_report_view.xml index 7c156599..c524f1c0 100644 --- a/indoteknik_custom/views/account_financial_report_view.xml +++ b/indoteknik_custom/views/account_financial_report_view.xml @@ -1,51 +1,15 @@ <?xml version="1.0" encoding="utf-8"?> <odoo> - - <record id="account_financial_report_view_custom" model="ir.ui.view"> - <field name="name">Common Report</field> + + <record id="custom_accounting_report_view" model="ir.ui.view"> + <field name="name">Accounting Report</field> <field name="model">accounting.report.xlsx</field> + <field name="inherit_id" ref="account_reports_xlsx.accounting_report_view"/> <field name="arch" type="xml"> - <form string="Report Options"> - <group col="4"> - <field name="account_report_id" domain="[('parent_id','=',False)]"/> - <field name="target_move" widget="radio"/> - <field name="enable_filter"/> - <field name="debit_credit" attrs="{'invisible': [('enable_filter','=',True)]}"/> - <field name="date_from"/> - <field name="date_to"/> - </group> - <group> - <notebook tabpos="up" colspan="4"> - <page string="Comparison" name="comparison" attrs="{'invisible': [('enable_filter','=',False)]}"> - <group> - <field name="label_filter" attrs="{'required': [('enable_filter', '=', True)]}"/> - <field name="filter_cmp"/> - </group> - <group string="Dates" attrs="{'invisible':[('filter_cmp', '!=', 'filter_date')]}"> - <field name="date_from_cmp" attrs="{'required':[('filter_cmp', '=', 'filter_date')]}"/> - <field name="date_to_cmp" attrs="{'required':[('filter_cmp', '=', 'filter_date')]}"/> - </group> - </page> - </notebook> - <field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/> - </group> - <footer> - <button name="check_report" string="Print" type="object" default_focus="1" class="oe_highlight"/> - <button string="Cancel" class="btn btn-secondary" special="cancel" /> - </footer> - </form> + <field name="date_to" position="after"> + <field name="by_month"/> + </field> </field> </record> - <record id="action_account_report_custom" model="ir.actions.act_window"> - <field name="name">Financial Reports</field> - <field name="res_model">accounting.report.xlsx</field> - <field name="type">ir.actions.act_window</field> - <field name="view_mode">form</field> - <field name="view_id" ref="account_financial_report_view_custom"/> - <field name="target">new</field> - </record> - - <menuitem id="menu_account_report" name="Financial Report" action="action_account_report_custom" parent="account.menu_finance_reports" sequence="250"/> - </odoo> diff --git a/indoteknik_custom/views/crm_lead.xml b/indoteknik_custom/views/crm_lead.xml index 58c3987a..9e4a43f2 100755 --- a/indoteknik_custom/views/crm_lead.xml +++ b/indoteknik_custom/views/crm_lead.xml @@ -47,6 +47,9 @@ <field name="message_bounce" position="after"> <field name="order_id"/> </field> + <field name="lang_id" position="after"> + <field name="reason"/> + </field> </field> </record> </data> diff --git a/indoteknik_custom/views/mail_template_po.xml b/indoteknik_custom/views/mail_template_po.xml index 410520b3..02053767 100644 --- a/indoteknik_custom/views/mail_template_po.xml +++ b/indoteknik_custom/views/mail_template_po.xml @@ -1,12 +1,12 @@ <?xml version="1.0" ?> <odoo> - <data noupdate="0"> + <data> <record id="mail_template_po_sync_price" model="mail.template"> <field name="name">PO: Sync Unit Price Purchase Pricelist</field> <field name="model_id" ref="model_purchase_order" /> <field name="subject">Your PO ${object.name}</field> <field name="email_from"></field> - <field name="email_to">darren@indoteknik.co.id</field> + <field name="email_to">darren@indoteknik.co.id, tyas@indoteknik.com, azkan4elll@gmail.com</field> <field name="body_html" type="html"> <table border="0" cellpadding="0" cellspacing="0" style="padding-top: 16px; background-color: #F1F1F1; font-family:Verdana, Arial,sans-serif; color: #454748; width: 100%; border-collapse:separate;"> @@ -45,13 +45,13 @@ <tr> <td valign="top" style="font-size: 13px;"> <div> - Dear Stefanus Darren, + Dear Stefanus Darren & Tyas K Putra, <br/><br/> Terdapat PO yang harga Unit Price nya tidak sama dengan yang ada di purchase pricelist nya. <br/><br/> Berikut adalah rincian PO: % for line in object.order_line: - % if line.price_vendor != 0 and line.price_unit != line.price_vendor + % if line.price_unit != line.price_vendor <ul> <li>Nama Produk: ${line.product_id.name}</li> <li>Harga Unit dalam PO: Rp ${'{:,.2f}'.format(line.price_unit)}</li> diff --git a/indoteknik_custom/views/product_pricelist.xml b/indoteknik_custom/views/product_pricelist.xml index 55139a24..34876cc4 100644 --- a/indoteknik_custom/views/product_pricelist.xml +++ b/indoteknik_custom/views/product_pricelist.xml @@ -25,4 +25,12 @@ </page> </field> </record> + + <record id="product_pricelist_sync_product_solr_ir_actions_server" model="ir.actions.server"> + <field name="name">Sync Product to SOLR</field> + <field name="model_id" ref="product.model_product_pricelist"/> + <field name="binding_model_id" ref="product.model_product_pricelist"/> + <field name="state">code</field> + <field name="code">action = records.update_product_solr_flag()</field> + </record> </odoo>
\ No newline at end of file |
