summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorit-fixcomart <it@fixcomart.co.id>2024-10-08 15:24:18 +0700
committerit-fixcomart <it@fixcomart.co.id>2024-10-08 15:24:18 +0700
commit2fcddbb073a04ed6ba0e9666a8ab5082a3ef4073 (patch)
treed4a3a423adf53ea8c086397abbcfe05a369fe637
parentec1e2331be248a505d89c00244d6b0fe5bd61c26 (diff)
parentce1c714c95f68b07f9b891600ba1e3b88288652c (diff)
Merge branch 'production' into iman/telegram
# Conflicts: # indoteknik_custom/models/__init__.py
-rw-r--r--indoteknik_api/controllers/api_v1/__init__.py3
-rw-r--r--indoteknik_api/controllers/api_v1/city.py12
-rw-r--r--indoteknik_api/controllers/api_v1/partner.py6
-rw-r--r--indoteknik_api/controllers/api_v1/state.py24
-rw-r--r--indoteknik_api/controllers/api_v1/user.py9
-rw-r--r--indoteknik_api/models/res_users.py9
-rwxr-xr-xindoteknik_custom/__manifest__.py1
-rwxr-xr-xindoteknik_custom/models/__init__.py2
-rw-r--r--indoteknik_custom/models/partner.py7
-rwxr-xr-xindoteknik_custom/models/purchase_order.py5
-rwxr-xr-xindoteknik_custom/models/sale_order.py154
-rw-r--r--indoteknik_custom/models/sale_order_line.py8
-rw-r--r--indoteknik_custom/models/stock_picking.py4
-rw-r--r--indoteknik_custom/models/vendor_approval.py73
-rw-r--r--indoteknik_custom/models/website_user_cart.py7
-rwxr-xr-xindoteknik_custom/security/ir.model.access.csv3
-rw-r--r--indoteknik_custom/views/ir_sequence.xml10
-rwxr-xr-xindoteknik_custom/views/sale_order.xml7
-rw-r--r--indoteknik_custom/views/vendor_approval.xml106
-rwxr-xr-xindoteknik_custom/views/vit_kota.xml2
20 files changed, 413 insertions, 39 deletions
diff --git a/indoteknik_api/controllers/api_v1/__init__.py b/indoteknik_api/controllers/api_v1/__init__.py
index 2d774071..6f27eec5 100644
--- a/indoteknik_api/controllers/api_v1/__init__.py
+++ b/indoteknik_api/controllers/api_v1/__init__.py
@@ -28,4 +28,5 @@ from . import midtrans
from . import wati
from . import courier
from . import voucher
-from . import stock_picking \ No newline at end of file
+from . import stock_picking
+from . import state \ No newline at end of file
diff --git a/indoteknik_api/controllers/api_v1/city.py b/indoteknik_api/controllers/api_v1/city.py
index 6e0e3edb..afe92c4a 100644
--- a/indoteknik_api/controllers/api_v1/city.py
+++ b/indoteknik_api/controllers/api_v1/city.py
@@ -15,10 +15,16 @@ class City(controller.Controller):
name = '%' + name.replace(' ', '%') + '%'
parameters.append(('name', 'ilike', name))
- cities = request.env['vit.kota'].search(parameters)
+ state_id = kw.get('state_id')
+ if state_id:
+ parameters.append(('state_id', '=', int(state_id)))
+
+ districts = request.env['vit.kota'].search(parameters)
data = []
- for city in cities:
- data.append({ 'id': city.id, 'name': city.name })
+ for district in districts:
+ data.append({ 'id': district.id, 'name': district.name })
return self.response(data)
+
+
diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py
index 833b3302..a7925a02 100644
--- a/indoteknik_api/controllers/api_v1/partner.py
+++ b/indoteknik_api/controllers/api_v1/partner.py
@@ -69,6 +69,7 @@ class Partner(controller.Controller):
'mobile': ['required'],
'phone': [''],
'street': ['required'],
+ 'state_id': ['required', 'number', 'alias:state_id'],
'city_id': ['required', 'number', 'alias:kota_id'],
'district_id': ['number', 'alias:kecamatan_id'],
'sub_district_id': ['number', 'alias:kelurahan_id', 'exclude_if_null'],
@@ -82,7 +83,7 @@ class Partner(controller.Controller):
partner = request.env[self._name].search([('id', '=', params['value']['id'])], limit=1)
if not partner:
return self.response(code=404, description='User not found')
-
+
partner.write(params['value'])
return self.response({
@@ -100,10 +101,11 @@ class Partner(controller.Controller):
'mobile': ['required'],
'phone': [''],
'street': ['required'],
+ 'state_id': ['required', 'number', 'alias:state_id'],
'city_id': ['required', 'number', 'alias:kota_id'],
'district_id': ['number', 'alias:kecamatan_id'],
'sub_district_id': ['number', 'alias:kelurahan_id', 'exclude_if_null'],
- 'zip': ['required'],
+ 'zip': ['required']
})
if not params['valid']:
diff --git a/indoteknik_api/controllers/api_v1/state.py b/indoteknik_api/controllers/api_v1/state.py
new file mode 100644
index 00000000..598ef70b
--- /dev/null
+++ b/indoteknik_api/controllers/api_v1/state.py
@@ -0,0 +1,24 @@
+from .. import controller
+from odoo import http
+from odoo.http import request
+
+class District(controller.Controller):
+ prefix = '/api/v1/'
+
+ @http.route(prefix + 'state', auth='public', methods=['GET', 'OPTIONS'])
+ @controller.Controller.must_authorized()
+ def get_state(self, **kw):
+ parameters = []
+
+ name = kw.get('name')
+ if name:
+ name = '%' + name.replace(' ', '%') + '%'
+ parameters.append(('name', 'ilike', name))
+
+ states = request.env['res.country.state'].search(parameters)
+ data = []
+ for state in states:
+ data.append({ 'id': state.id, 'name': state.name })
+
+ return self.response(data)
+
diff --git a/indoteknik_api/controllers/api_v1/user.py b/indoteknik_api/controllers/api_v1/user.py
index 52d773e2..c7bfe91a 100644
--- a/indoteknik_api/controllers/api_v1/user.py
+++ b/indoteknik_api/controllers/api_v1/user.py
@@ -94,7 +94,13 @@ class User(controller.Controller):
user = request.env['res.users'].create(user_data)
user.partner_id.email = email
-
+ user.partner_id.customer_type = 'nonpkp'
+ user.partner_id.npwp = '0.000.000.0-000.000'
+ user.partner_id.sppkp = '-'
+ user.partner_id.nama_wajib_pajak = user.name
+ user.partner_id.user_id = 3222
+ user.partner_id.property_account_receivable_id = 395
+ user.partner_id.property_account_payable_id = 438
data = {
'is_auth': True,
'user': self.response_with_token(user)
@@ -149,6 +155,7 @@ class User(controller.Controller):
'name': name,
'login': email,
'mobile': phone,
+ 'phone': phone,
'password': password,
'active': False,
'sel_groups_1_9_10': 9
diff --git a/indoteknik_api/models/res_users.py b/indoteknik_api/models/res_users.py
index b2e8acfe..534898e1 100644
--- a/indoteknik_api/models/res_users.py
+++ b/indoteknik_api/models/res_users.py
@@ -48,6 +48,7 @@ class ResUsers(models.Model):
'street': user.street or '',
'street2': user.street2 or '',
'city': None,
+ 'state_id': None,
'district': None,
'sub_district': None,
'zip': user.zip or '',
@@ -60,12 +61,18 @@ class ResUsers(models.Model):
'alamat_bisnis': user.street or None,
}
+ if user.state_id:
+ data['state_id'] = {
+ 'id': user.state_id.id,
+ 'name': user.state_id.name
+ } or None
+
if user.kota_id:
data['city'] = {
'id': user.kota_id.id,
'name': user.kota_id.name
} or None
-
+
if user.kecamatan_id:
data['district'] = {
'id': user.kecamatan_id.id,
diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py
index 3104b9a3..234ccebb 100755
--- a/indoteknik_custom/__manifest__.py
+++ b/indoteknik_custom/__manifest__.py
@@ -144,6 +144,7 @@
'views/partner_payment_term.xml',
'views/vendor_payment_term.xml',
'views/approval_unreserve.xml',
+ 'views/vendor_approval.xml',
'report/report.xml',
'report/report_banner_banner.xml',
'report/report_banner_banner2.xml',
diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py
index 1620c82e..13c48673 100755
--- a/indoteknik_custom/models/__init__.py
+++ b/indoteknik_custom/models/__init__.py
@@ -128,4 +128,6 @@ from . import sales_order_reject
from . import approval_date_doc
from . import account_tax
from . import approval_unreserve
+from . import vendor_approval
+from . import partner
from . import website_telegram
diff --git a/indoteknik_custom/models/partner.py b/indoteknik_custom/models/partner.py
new file mode 100644
index 00000000..46dc751a
--- /dev/null
+++ b/indoteknik_custom/models/partner.py
@@ -0,0 +1,7 @@
+from odoo import fields, models, api, _
+from odoo.exceptions import AccessError, UserError, ValidationError
+
+class kota(models.Model):
+ _inherit = 'vit.kota'
+
+ is_jabodetabek = fields.Boolean(string='Jabodetabek', default=False) \ No newline at end of file
diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py
index 8a47482a..08408506 100755
--- a/indoteknik_custom/models/purchase_order.py
+++ b/indoteknik_custom/models/purchase_order.py
@@ -503,6 +503,9 @@ class PurchaseOrder(models.Model):
current_time = datetime.now()
self.check_ppn_mix()
# self.check_data_vendor()
+
+ if self.amount_untaxed >= 50000000 and not self.env.user.has_group('indoteknik_custom.group_role_merchandiser'):
+ raise UserError("Hanya Merchandiser yang bisa approve")
if self.total_percent_margin < self.total_so_percent_margin and not self.env.user.is_purchasing_manager and not self.env.user.is_leader:
raise UserError("Beda Margin dengan Sales, harus approval Manager")
@@ -634,6 +637,8 @@ class PurchaseOrder(models.Model):
template.send_mail(self.id, force_send=True)
def po_approve(self):
+ if self.amount_untaxed >= 50000000 and not self.env.user.has_group('indoteknik_custom.group_role_merchandiser'):
+ raise UserError("Hanya Merchandiser yang bisa approve")
if self.env.user.is_leader or self.env.user.is_purchasing_manager:
raise UserError("Bisa langsung Confirm")
elif self.total_percent_margin == self.total_so_percent_margin and self.sale_order_id:
diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py
index f8127d8c..c1c2c267 100755
--- a/indoteknik_custom/models/sale_order.py
+++ b/indoteknik_custom/models/sale_order.py
@@ -78,6 +78,7 @@ class SaleOrder(models.Model):
payment_link_midtrans = fields.Char(string='Payment Link', help='Url payment yg digenerate oleh midtrans, harap diserahkan ke customer agar dapat dilakukan pembayaran secara mandiri')
payment_qr_code = fields.Binary("Payment QR Code")
due_id = fields.Many2one('due.extension', string="Due Extension", readonly=True, tracking=True)
+ vendor_approval_id = fields.Many2one('vendor.approval', string="Vendor Approval", readonly=True, tracking=True)
customer_type = fields.Selection([
('pkp', 'PKP'),
('nonpkp', 'Non PKP')
@@ -104,6 +105,7 @@ class SaleOrder(models.Model):
('cust_procurement', 'Customer Procurement')
], string='Web Approval', copy=False)
compute_fullfillment = fields.Boolean(string='Compute Fullfillment', compute="_compute_fullfillment")
+ vendor_approval = fields.Boolean(string='Vendor Approval')
note_ekspedisi = fields.Char(string="Note Ekspedisi")
date_kirim_ril = fields.Datetime(string='Tanggal Kirim SJ', compute='_compute_date_kirim', copy=False)
date_status_done = fields.Datetime(string='Date Done DO', compute='_compute_date_kirim', copy=False)
@@ -133,59 +135,105 @@ class SaleOrder(models.Model):
'account.payment.term', string='Payment Terms', check_company=True, # Unrequired company
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]", tracking=True)
- def action_estimate_shipping(self):
+ total_weight = fields.Float(string='Total Weight', compute='_compute_total_weight')
+
+ def _compute_total_weight(self):
total_weight = 0
missing_weight_products = []
for line in self.order_line:
if line.weight:
- total_weight += line.weight
+ total_weight += line.weight * line.product_uom_qty
+
+ self.total_weight = total_weight
+
+ def action_indoteknik_estimate_shipping(self):
+ if not self.real_shipping_id.kota_id.is_jabodetabek:
+ raise UserError('Estimasi ongkir hanya bisa dilakukan di kota Jabodetabek')
+
+ total_weight = 0
+ missing_weight_products = []
+
+ for line in self.order_line:
+ if line.weight:
+ total_weight += line.weight * line.product_uom_qty
line.product_id.weight = line.weight
else:
missing_weight_products.append(line.product_id.name)
if missing_weight_products:
- product_names = '\n'.join(missing_weight_products)
- self.message_post(body=f"Produk berikut tidak memiliki berat: \n{product_names}")
+ product_names = '<br/>'.join(missing_weight_products)
+ self.message_post(body=f"Produk berikut tidak memiliki berat:<br/>{product_names}")
+
+ if total_weight == 0:
+ raise UserError("Tidak dapat mengestimasi ongkir tanpa berat yang valid.")
+
+ if total_weight < 10:
+ total_weight = 10
+ self.delivery_amt = total_weight * 3000
+
+ def action_estimate_shipping(self):
+ if self.carrier_id.id in [1, 151]:
+ self.action_indoteknik_estimate_shipping()
+ return
+ total_weight = 0
+ missing_weight_products = []
+ for line in self.order_line:
+ if line.weight:
+ total_weight += line.weight * line.product_uom_qty
+ line.product_id.weight = line.weight
+ else:
+ missing_weight_products.append(line.product_id.name)
+
+ if missing_weight_products:
+ product_names = '<br/>'.join(missing_weight_products)
+ self.message_post(body=f"Produk berikut tidak memiliki berat:<br/>{product_names}")
if total_weight == 0:
raise UserError("Tidak dapat mengestimasi ongkir tanpa berat yang valid.")
# Mendapatkan city_id berdasarkan nama kota
origin_city_name = self.warehouse_id.partner_id.kota_id.name
- destination_city_name = self.real_shipping_id.kota_id.name
- origin_subdistrict_name = self.warehouse_id.partner_id.kecamatan_id.name
- destination_subdistrict_name = self.real_shipping_id.kecamatan_id.name
-
- origin_id_city = self._get_city_id_by_name(origin_city_name)
- destination_id_city = self._get_city_id_by_name(destination_city_name)
- origin_city_id = self._get_subdistrict_id_by_name(origin_id_city, origin_subdistrict_name)
- destination_city_id = self._get_subdistrict_id_by_name(destination_id_city, destination_subdistrict_name)
+ destination_subsdistrict_id = self.real_shipping_id.kecamatan_id.rajaongkir_id
- if not origin_city_id or not destination_city_id:
+ if not destination_subsdistrict_id:
raise UserError("Gagal mendapatkan ID kota asal atau tujuan.")
- result = self._call_rajaongkir_api(total_weight, origin_city_id, destination_city_id)
+ result = self._call_rajaongkir_api(total_weight, destination_subsdistrict_id)
if result:
estimated_cost = result['rajaongkir']['results'][0]['costs'][0]['cost'][0]['value']
self.delivery_amt = estimated_cost
- self.message_post(body=f"Estimasi Ongkos Kirim: {self.delivery_amt}")
+ shipping_info = []
+ for courier in result['rajaongkir']['results']:
+ for cost_detail in courier['costs']:
+ service = cost_detail['service']
+ description = cost_detail['description']
+ etd = cost_detail['cost'][0]['etd']
+ value = cost_detail['cost'][0]['value']
+ shipping_info.append(f"Service: {service}, Description: {description}, ETD: {etd} hari, Cost: Rp {value}")
+
+ log_message = "<br/>".join(shipping_info)
+
+ description_ongkir = result['rajaongkir']['results'][0]['costs'][0]['description']
+ etd_ongkir = result['rajaongkir']['results'][0]['costs'][0]['cost'][0]['etd']
+ service_ongkir = result['rajaongkir']['results'][0]['costs'][0]['service']
+ self.message_post(body=f"Estimasi Ongkos Kirim: Rp{self.delivery_amt}<br/>Service: {service_ongkir}<br/>Description: {description_ongkir}<br/>ETD: {etd_ongkir}<br/>Detail Lain:<br/>{log_message}")
else:
raise UserError("Gagal mendapatkan estimasi ongkir.")
- def _call_rajaongkir_api(self, total_weight, origin_city_id, destination_city_id):
+ def _call_rajaongkir_api(self, total_weight, destination_subsdistrict_id):
url = 'https://pro.rajaongkir.com/api/cost'
headers = {
- 'key': '7ac9883688da043b50cc32f0e3070bb6',
+ 'key': '9b1310f644056d84d60b0af6bb21611a',
}
courier = self.carrier_id.name.lower()
data = {
- 'origin': int(origin_city_id),
+ 'origin': 2127,
'originType': 'subdistrict',
- 'destination': int(destination_city_id),
+ 'destination': int(destination_subsdistrict_id),
'destinationType': 'subdistrict',
'weight': int(total_weight * 1000),
'courier': courier,
@@ -211,7 +259,7 @@ class SaleOrder(models.Model):
def _get_city_id_by_name(self, city_name):
url = 'https://pro.rajaongkir.com/api/city'
headers = {
- 'key': '7ac9883688da043b50cc32f0e3070bb6',
+ 'key': '9b1310f644056d84d60b0af6bb21611a',
}
normalized_city_name = self._normalize_city_name(city_name)
@@ -227,14 +275,17 @@ class SaleOrder(models.Model):
def _get_subdistrict_id_by_name(self, city_id, subdistrict_name):
url = f'https://pro.rajaongkir.com/api/subdistrict?city={city_id}'
headers = {
- 'key': '7ac9883688da043b50cc32f0e3070bb6',
+ 'key': '9b1310f644056d84d60b0af6bb21611a',
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
subdistrict_data = response.json()
for subdistrict in subdistrict_data['rajaongkir']['results']:
- if subdistrict['subdistrict_name'].lower() == subdistrict_name.lower():
+ subsdistrict_1 = subdistrict['subdistrict_name'].lower()
+ subsdistrict_2 = subdistrict_name.lower()
+
+ if subsdistrict_1 == subsdistrict_2:
return subdistrict['subdistrict_id']
return None
@@ -682,10 +733,14 @@ class SaleOrder(models.Model):
for order in self:
order.order_line.validate_line()
+ term_days = 0
+ for term_line in order.payment_term_id.line_ids:
+ term_days += term_line.days
+
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:
+ if not partner.active_limit and term_days > 0:
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")
@@ -699,16 +754,23 @@ class SaleOrder(models.Model):
raise UserError("Salesperson sudah tidak aktif, mohon diisi yang benar pada data SO dan Contact")
def sale_order_approve(self):
+ if self.validate_different_vendor() and not self.vendor_approval and not self.vendor_approval_id:
+ return self._create_notification_action('Notification', 'Terdapat Vendor yang berbeda dengan MD Vendor')
self.check_due()
self._validate_order()
for order in self:
order.order_line.validate_line()
+
+ term_days = 0
+ for term_line in order.payment_term_id.line_ids:
+ term_days += term_line.days
+
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:
+ if not partner.active_limit and term_days > 0:
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")
@@ -802,9 +864,53 @@ class SaleOrder(models.Model):
'body_html': email_body,
'email_to': salesperson_email,
}).send()
+
+ def validate_different_vendor(self):
+ different_vendor = self.order_line.filtered(lambda l: l.vendor_id and l.vendor_md_id and l.vendor_id.id != l.vendor_md_id.id)
+
+ if self.vendor_approval_id and self.vendor_approval_id.state == 'draft':
+ raise UserError('SO ini sedang dalam review Vendor Approval')
+
+ if self.vendor_approval_id and self.vendor_approval_id.state == 'cancel':
+ raise UserError('Vendor Approval SO ini Di Reject')
+
+ if different_vendor:
+ vendor_approval = self.env['vendor.approval'].create({
+ 'order_id': self.id,
+ 'create_date_so': self.create_date,
+ 'partner_id': self.partner_id.id,
+ 'state': 'draft',
+ })
+
+ self.vendor_approval_id = vendor_approval.id
+
+ for line in different_vendor:
+ self.env['vendor.approval.line'].create({
+ 'vendor_approval_id': vendor_approval.id,
+ 'product_id': line.product_id.id,
+ 'product_uom_qty': line.product_uom_qty,
+ 'vendor_id': line.vendor_id.id,
+ 'vendor_md_id': line.vendor_md_id.id,
+ 'purchase_price': line.purchase_price,
+ 'purchase_price_md': line.purchase_price_md,
+ 'sales_price': line.price_unit,
+ 'margin_before': line.margin_md,
+ 'margin_after': line.item_percent_margin,
+ 'purchase_tax_id': line.purchase_tax_id.id,
+ 'sales_tax_id': line.tax_id[0].id if line.tax_id else False,
+ 'percent_margin_difference': (line.price_unit - line.purchase_price_md) / line.purchase_price_md if line.purchase_price_md else False,
+ })
+
+ return True
+ else:
+ return False
+
def action_confirm(self):
for order in self:
+ if self.validate_different_vendor() and not self.vendor_approval and not self.vendor_approval_id:
+ return self._create_notification_action('Notification', 'Terdapat Vendor yang berbeda dengan MD Vendor')
+
order.check_data_real_delivery_address()
order.sale_order_check_approve()
order._validate_order()
diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py
index 14bb8c99..59ac1ad4 100644
--- a/indoteknik_custom/models/sale_order_line.py
+++ b/indoteknik_custom/models/sale_order_line.py
@@ -14,7 +14,9 @@ class SaleOrderLine(models.Model):
states={'draft': [('readonly', False)], 'sent': [('readonly', False)]},
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]"
)
+ vendor_md_id = fields.Many2one('res.partner', string='MD Vendor')
purchase_price = fields.Float('Purchase', required=True, digits='Product Price', default=0.0)
+ purchase_price_md = fields.Float('MD Purchase')
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)
@@ -33,6 +35,7 @@ class SaleOrderLine(models.Model):
item_percent_margin_without_deduction = fields.Float('%Margin', compute='_compute_item_margin_without_deduction')
weight = fields.Float(string='Weight')
md_vendor_id = fields.Many2one('res.partner', string='MD Vendor', readonly=True)
+ margin_md = fields.Float(string='Margin MD')
@api.constrains('note_procurement')
def note_procurement_to_apo(self):
@@ -126,6 +129,9 @@ class SaleOrderLine(models.Model):
else:
line.item_percent_margin = 0
+ if not line.margin_md:
+ line.margin_md = line.item_percent_margin
+
@api.onchange('vendor_id')
def onchange_vendor_id(self):
# TODO : need to change this logic @stephan
@@ -247,8 +253,10 @@ class SaleOrderLine(models.Model):
price, taxes, vendor_id = self._get_purchase_price(line.product_id)
line.vendor_id = vendor_id
line.md_vendor_id = vendor_id
+ line.margin_md = line.item_percent_margin
line.tax_id = line.order_id.sales_tax_id
# price, taxes = line._get_valid_purchase_price(purchase_price)
+ line.purchase_price_md = price
line.purchase_price = price
line.purchase_tax_id = taxes
diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py
index 6f038386..14190474 100644
--- a/indoteknik_custom/models/stock_picking.py
+++ b/indoteknik_custom/models/stock_picking.py
@@ -125,7 +125,9 @@ class StockPicking(models.Model):
raise UserError('Hanya Logistic yang bisa mengubah shipping method')
def do_unreserve(self):
- if not self._context.get('darimana') == 'sale.order':
+ group_id = self.env.ref('indoteknik_custom.group_role_it').id
+ users_in_group = self.env['res.users'].search([('groups_id', 'in', [group_id])])
+ if not self._context.get('darimana') == 'sale.order' and self.env.user.id not in users_in_group.mapped('id'):
self.sale_id.unreserve_id = self.id
return self._create_approval_notification('Logistic')
diff --git a/indoteknik_custom/models/vendor_approval.py b/indoteknik_custom/models/vendor_approval.py
new file mode 100644
index 00000000..e540b8fc
--- /dev/null
+++ b/indoteknik_custom/models/vendor_approval.py
@@ -0,0 +1,73 @@
+from odoo import models, api, fields
+from odoo.exceptions import AccessError, UserError, ValidationError
+from datetime import timedelta, date
+import logging
+
+_logger = logging.getLogger(__name__)
+
+class VendorApproval(models.Model):
+ _name = "vendor.approval"
+ _description = "Vendor Approval"
+ _inherit = ['mail.thread']
+ _rec_name = 'number'
+
+ number = fields.Char(string='Document No', index=True, copy=False, readonly=True, tracking=True)
+ partner_id = fields.Many2one('res.partner', string="Customer", readonly=True)
+ order_id = fields.Many2one('sale.order', string="SO", readonly=True)
+ vendor_approval_line = fields.One2many('vendor.approval.line', 'vendor_approval_id', string='Vendor Approval Lines', auto_join=True)
+ state = fields.Selection([('draft', 'Draft'), ('done', 'Done'), ('cancel', 'Reject')], string='State', tracking=True)
+ create_date_so = fields.Datetime(string='Create Date SO', readonly=True)
+
+ @api.model
+ def create(self, vals):
+ vals['number'] = self.env['ir.sequence'].next_by_code('vendor.approval') or '0'
+ result = super(VendorApproval, self).create(vals)
+ return result
+
+ def action_approve(self):
+ if not self.env.user.has_group('indoteknik_custom.group_role_merchandiser'):
+ raise UserError('Hanya Merchandiser yang bisa approve')
+
+ self.state = 'done'
+ self.order_id.update({'vendor_approval': True})
+ self.order_id.action_confirm()
+ message = "Vendor Approval approved by %s" % (self.env.user.name)
+ self.order_id.message_post(body=message)
+
+
+ def action_reject(self):
+ if not self.env.user.has_group('indoteknik_custom.group_role_merchandiser'):
+ raise UserError('Hanya Merchandiser yang bisa cancel')
+
+ self.state = 'cancel'
+
+ message = "Vendor Approval rejected by %s" % (self.env.user.name)
+ self.order_id.message_post(body=message)
+
+ def unlink(self):
+ res = super(VendorApproval, self).unlink()
+ if not self._name == 'vendor.approval':
+ raise UserError('Vendor Approval tidak bisa didelete')
+ return res
+
+
+class VendorApprovalLine(models.Model):
+ _name = 'vendor.approval.line'
+ _description = 'Vendor Approval Line'
+ _order = 'vendor_approval_id, id'
+
+ vendor_approval_id = fields.Many2one('vendor.approval', string='Vendor Approval Ref', required=True, ondelete='cascade', index=True, copy=False)
+ product_id = fields.Many2one('product.product', string='Product')
+ product_uom_qty = fields.Float(string='Quantity')
+ vendor_id = fields.Many2one('res.partner', string='Vendor')
+ vendor_md_id = fields.Many2one('res.partner', string='Vendor MD')
+ sales_price = fields.Float(string='Sales Price')
+ margin_before = fields.Float(string='Margin Before')
+ margin_after = fields.Float(string='Margin After')
+ purchase_price = fields.Float(string='Purchase Price')
+ purchase_price_md= fields.Float(string='Purchase Price MD')
+ purchase_tax_id = fields.Many2one('account.tax', string='Purchase Tax', domain=['|', ('active', '=', False), ('active', '=', True)])
+ sales_tax_id = fields.Many2one('account.tax', string='Sales Tax', domain=['|', ('active', '=', False), ('active', '=', True)])
+ percent_margin_difference = fields.Float(string='Percent Margin Difference')
+
+
diff --git a/indoteknik_custom/models/website_user_cart.py b/indoteknik_custom/models/website_user_cart.py
index 169f4a6b..76b192c5 100644
--- a/indoteknik_custom/models/website_user_cart.py
+++ b/indoteknik_custom/models/website_user_cart.py
@@ -173,13 +173,12 @@ class WebsiteUserCart(models.Model):
}
return result
- def action_mail_reminder_to_checkout(self, limit=10):
- user_ids = self.search([]).mapped('user_id')[:limit]
+ def action_mail_reminder_to_checkout(self, limit=250):
+ user_ids = self.search([('is_reminder', '=', False)]).mapped('user_id')[:limit]
for user in user_ids:
latest_cart = self.search([('user_id', '=', user.id), ('is_reminder', '=', False)], order='create_date desc', limit=1)
- # Proses semua keranjang untuk user tersebut
carts_to_remind = self.search([('user_id', '=', user.id)])
if latest_cart and not latest_cart.is_reminder:
@@ -191,11 +190,9 @@ class WebsiteUserCart(models.Model):
cart.is_selected = False
cart.is_reminder = True
- # Mengirim email pengingat untuk keranjang terbaru
template = self.env.ref('indoteknik_custom.mail_template_user_cart_reminder_to_checkout')
template.send_mail(latest_cart.id, force_send=True)
-
def calculate_discount(self, user_id):
carts = self.search([('user_id', '=', user_id)])
voucher = self.env['voucher'].browse(146)
diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv
index 84147e04..80ae0afa 100755
--- a/indoteknik_custom/security/ir.model.access.csv
+++ b/indoteknik_custom/security/ir.model.access.csv
@@ -139,3 +139,6 @@ access_approval_date_doc,access.approval.date.doc,model_approval_date_doc,,1,1,1
access_account_tax,access.account.tax,model_account_tax,,1,1,1,1
access_approval_unreserve,access.approval.unreserve,model_approval_unreserve,,1,1,1,1
access_approval_unreserve_line,access.approval.unreserve.line,model_approval_unreserve_line,,1,1,1,1
+access_vendor_approval,access.vendor.approval,model_vendor_approval,,1,1,1,1
+access_vendor_approval_line,access.vendor.approval.line,model_vendor_approval_line,,1,1,1,1
+access_vit_kota,access.vit.kota,model_vit_kota,,1,1,1,1
diff --git a/indoteknik_custom/views/ir_sequence.xml b/indoteknik_custom/views/ir_sequence.xml
index dd501d8c..dfb56100 100644
--- a/indoteknik_custom/views/ir_sequence.xml
+++ b/indoteknik_custom/views/ir_sequence.xml
@@ -21,6 +21,16 @@
<field name="number_increment">1</field>
</record>
+ <record id="sequence_vendor_approval" model="ir.sequence">
+ <field name="name">Vendor Approval</field>
+ <field name="code">vendor.approval</field>
+ <field name="active">TRUE</field>
+ <field name="prefix">VA/%(year)s/</field>
+ <field name="padding">5</field>
+ <field name="number_next">1</field>
+ <field name="number_increment">1</field>
+ </record>
+
<record id="sequence_approval_unreserve" model="ir.sequence">
<field name="name">Approval Unreserve</field>
<field name="code">approval.unreserve</field>
diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml
index 1c75c96a..895b242c 100755
--- a/indoteknik_custom/views/sale_order.xml
+++ b/indoteknik_custom/views/sale_order.xml
@@ -72,6 +72,7 @@
<field name="flash_sale"/>
<field name="margin_after_delivery_purchase"/>
<field name="percent_margin_after_delivery_purchase"/>
+ <field name="total_weight"/>
</field>
<field name="analytic_account_id" position="after">
<field name="customer_type" required="1"/>
@@ -80,6 +81,7 @@
<field name="email" required="1"/>
<field name="unreserve_id"/>
<field name="due_id" readonly="1"/>
+ <field name="vendor_approval_id" readonly="1"/>
<field name="source_id" domain="[('id', 'in', [32, 59, 60, 61])]" required="1"/>
<button name="override_allow_create_invoice"
string="Override Create Invoice"
@@ -117,6 +119,7 @@
</xpath>
<xpath expr="//form/sheet/notebook/page/field[@name='order_line']/tree/field[@name='price_total']" position="after">
<field name="vendor_id" attrs="{'readonly': [('parent.approval_status', '=', 'approved')]}" domain="[('parent_id', '=', False)]" options="{'no_create':True}"/>
+ <field name="vendor_md_id" optional="hide"/>
<field name="purchase_price" attrs="
{
'readonly': [
@@ -126,13 +129,15 @@
]
}
"/>
+ <field name="purchase_price_md" optional="hide"/>
<field name="purchase_tax_id" attrs="{'readonly': [('parent.approval_status', '!=', False)]}" domain="[('type_tax_use','=','purchase')]" options="{'no_create':True}"/>
<field name="item_percent_margin"/>
<field name="item_margin" optional="hide"/>
+ <field name="margin_md" optional="hide"/>
<field name="note" optional="hide"/>
<field name="note_procurement" optional="hide"/>
<field name="vendor_subtotal" optional="hide"/>
- <field name="weight" optional="hide"/>
+ <field name="weight" optional="hide"/>
<field name="amount_voucher_disc" string="Voucher" readonly="1" optional="hide"/>
<field name="order_promotion_id" string="Promotion" readonly="1" optional="hide"/>
<field name="md_vendor_id" string="MD Vendor" readonly="1" optional="hide"/>
diff --git a/indoteknik_custom/views/vendor_approval.xml b/indoteknik_custom/views/vendor_approval.xml
new file mode 100644
index 00000000..605edfbf
--- /dev/null
+++ b/indoteknik_custom/views/vendor_approval.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<odoo>
+ <data>
+ <record id="vendor_approval_tree" model="ir.ui.view">
+ <field name="name">vendor.approval.tree</field>
+ <field name="model">vendor.approval</field>
+ <field name="arch" type="xml">
+ <tree default_order="create_date desc" create="0">
+ <field name="number"/>
+ <field name="create_date_so"/>
+ <field name="order_id"/>
+ <field name="partner_id"/>
+ <field name="state"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="vendor_approval_line_tree" model="ir.ui.view">
+ <field name="name">vendor.approval.line.tree</field>
+ <field name="model">vendor.approval.line</field>
+ <field name="arch" type="xml">
+ <tree>
+ <field name="product_id"/>
+ <field name="sales_price"/>
+ <field name="product_uom_qty"/>
+ <field name="sales_tax_id"/>
+ <field name="margin_after"/>
+ <field name="vendor_id"/>
+ <field name="vendor_md_id"/>
+ <field name="purchase_price"/>
+ <field name="purchase_price_md"/>
+ <field name="margin_before"/>
+ <field name="purchase_tax_id"/>
+ <field name="percent_margin_difference"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="vendor_approval_form" model="ir.ui.view">
+ <field name="name">vendor.approval.form</field>
+ <field name="model">vendor.approval</field>
+ <field name="arch" type="xml">
+ <form create="false">
+ <header>
+ <button name="action_approve"
+ string="Approve"
+ type="object"
+ attrs="{'invisible': [('state', 'not in', ['draft'])]}"
+ />
+ <button name="action_reject"
+ string="Reject"
+ type="object"
+ attrs="{'invisible': [('state', 'not in', ['draft'])]}"
+ />
+ </header>
+ <sheet>
+ <group>
+ <group>
+ <field name="partner_id" readonly="1"/>
+ <field name="order_id" readonly="1"/>
+ <field name="state" readonly="1"/>
+ <field name="create_date_so" readonly="1"/>
+ </group>
+ </group>
+ <notebook>
+ <page string="SO Line">
+ <field name="vendor_approval_line" readonly="1"/>
+ </page>
+ </notebook>
+ </sheet>
+ <div class="oe_chatter">
+ <field name="message_follower_ids" widget="mail_followers"/>
+ <field name="message_ids" widget="mail_thread"/>
+ </div>
+ </form>
+ </field>
+ </record>
+
+ <record id="vendor_approval_view_search" model="ir.ui.view">
+ <field name="name">vendor.approval.search.view</field> <!-- Made the name more descriptive -->
+ <field name="model">vendor.approval</field>
+ <field name="arch" type="xml">
+ <search string="Search Vendor Approval">
+ <field name="number"/>
+ <field name="partner_id"/>
+ <field name="order_id"/>
+ </search>
+ </field>
+ </record>
+
+ <record id="vendor_approval_action" model="ir.actions.act_window">
+ <field name="name">Vendor Approval</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">vendor.approval</field>
+ <field name="view_mode">tree,form</field>
+ </record>
+
+ <menuitem
+ id="menu_vendor_approval"
+ name="Vendor Approval"
+ parent="sale.product_menu_catalog"
+ sequence="4"
+ action="vendor_approval_action"
+ />
+ </data>
+</odoo>
diff --git a/indoteknik_custom/views/vit_kota.xml b/indoteknik_custom/views/vit_kota.xml
index 97c7e66c..58c97eb4 100755
--- a/indoteknik_custom/views/vit_kota.xml
+++ b/indoteknik_custom/views/vit_kota.xml
@@ -9,6 +9,7 @@
<field name="name"/>
<field name="jenis"/>
<field name="state_id"/>
+ <field name="is_jabodetabek"/>
</tree>
</field>
</record>
@@ -28,6 +29,7 @@
<group>
<field name="jenis"/>
<field name="state_id"/>
+ <field name="is_jabodetabek"/>
</group>
<group/>
</group>