summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIT Fixcomart <it@fixcomart.co.id>2023-08-16 04:51:39 +0000
committerIT Fixcomart <it@fixcomart.co.id>2023-08-16 04:51:39 +0000
commitb0dd1bde531a90a867448c03ec08b72cc70f0053 (patch)
tree0ec22360367159e8b928b030351d6a8b53f3ada4
parent9f8664c33429e41c6133ba2b325662245ffbc92d (diff)
parenta73c0825a264fc002e7496b07f238f79efd93086 (diff)
Merged in refactor (pull request #99)
Refactor
-rw-r--r--indoteknik_api/controllers/controller.py22
-rwxr-xr-xindoteknik_custom/models/__init__.py1
-rw-r--r--indoteknik_custom/models/account_financial_report.py164
-rw-r--r--indoteknik_custom/models/account_report_financial.py8
-rwxr-xr-xindoteknik_custom/models/crm_lead.py21
-rw-r--r--indoteknik_custom/models/performance_test.py20
-rw-r--r--indoteknik_custom/models/product_pricelist.py18
-rwxr-xr-xindoteknik_custom/models/purchase_order.py2
-rw-r--r--indoteknik_custom/models/res_partner.py7
-rwxr-xr-xindoteknik_custom/models/sale_order.py600
-rw-r--r--indoteknik_custom/models/sale_order_line.py182
-rw-r--r--indoteknik_custom/views/account_financial_report_view.xml50
-rwxr-xr-xindoteknik_custom/views/crm_lead.xml3
-rw-r--r--indoteknik_custom/views/mail_template_po.xml8
-rw-r--r--indoteknik_custom/views/product_pricelist.xml8
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 &amp; 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