From afda28eb5bcda235c3c0b9479884894f9cd88c8c Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 1 Oct 2024 17:30:48 +0700 Subject: add inrernal input --- indoteknik_custom/models/user_company_request.py | 20 +++++++++++++++++++- indoteknik_custom/views/user_company_request.xml | 4 ++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/user_company_request.py b/indoteknik_custom/models/user_company_request.py index d540b0f6..f86f3872 100644 --- a/indoteknik_custom/models/user_company_request.py +++ b/indoteknik_custom/models/user_company_request.py @@ -1,4 +1,4 @@ -from odoo import models, fields +from odoo import models, fields, api from odoo.exceptions import UserError from odoo.http import request @@ -13,14 +13,32 @@ class UserCompanyRequest(models.Model): ('approved', 'Approve'), ('rejected', 'Reject'), ], string='Approval') + internal_input = fields.Char(string='Internal Input') + company_type = fields.Char(string='Company Type', compute='_compute_company_type') + + @api.depends('user_company_id.customer_type') + def _compute_company_type(self): + for record in self: + if record.user_company_id.customer_type == 'nonpkp': + record.company_type = 'Non PKP' + elif record.user_company_id.customer_type == 'pkp': + record.company_type = 'PKP' + else: + record.company_type = 'company type belum di set' def write(self, vals): user = self.get_user_by_email(self.user_id.email) user.parent_name = self.user_input is_approve = vals.get('is_approve') + is_internal_input = vals.get('internal_input') if self.is_approve and is_approve: raise UserError('Tidak dapat mengubah approval yang sudah diisi') + if is_internal_input: + if self.user_company_id.nama_wajib_pajak == self.user_company_id.name: + self.user_company_id.nama_wajib_pajak = is_internal_input + self.user_company_id.name = is_internal_input + if not self.is_approve and is_approve: if is_approve == 'approved': self.user_id.parent_id = self.user_company_id.id diff --git a/indoteknik_custom/views/user_company_request.xml b/indoteknik_custom/views/user_company_request.xml index c9edd3f8..bff06ecd 100644 --- a/indoteknik_custom/views/user_company_request.xml +++ b/indoteknik_custom/views/user_company_request.xml @@ -14,6 +14,8 @@ decoration-success="is_approve == 'approved'" decoration-danger="is_approve == 'rejected'" /> + + @@ -36,6 +38,8 @@ decoration-success="is_approve == 'approved'" decoration-danger="is_approve == 'rejected'" /> + + -- cgit v1.2.3 From a2d93b35cc849d742e851f5d876557ac18c1a9e1 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 2 Oct 2024 10:41:13 +0700 Subject: cr function get id partner --- indoteknik_api/controllers/api_v1/partner.py | 1 + indoteknik_custom/models/sale_order.py | 26 ++++++++++++++++---------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py index 369f2125..833b3302 100644 --- a/indoteknik_api/controllers/api_v1/partner.py +++ b/indoteknik_api/controllers/api_v1/partner.py @@ -13,6 +13,7 @@ class Partner(controller.Controller): partner_child_ids = [x['id'] for x in partner.child_ids] + [partner.id] if partner.parent_id: partner_child_ids = [x['id'] for x in partner.parent_id.child_ids] + partner_child_ids += [partner.parent_id.id] return partner_child_ids @http.route(prefix + 'partner//list/site', auth='public', methods=['GET', 'OPTIONS']) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 8f48e898..8c506c47 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -140,18 +140,27 @@ class SaleOrder(models.Model): for line in self.order_line: if line.weight: total_weight += line.weight + line.product_id.weight = line.weight else: missing_weight_products.append(line.product_id.name) + if missing_weight_products: + product_names = ', '.join(missing_weight_products) + self.message_post(body=f"Produk berikut tidak memiliki berat: {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_city_id = self._get_city_id_by_name(origin_city_name) - destination_city_id = self._get_city_id_by_name(destination_city_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) if not origin_city_id or not destination_city_id: raise UserError("Gagal mendapatkan ID kota asal atau tujuan.") @@ -160,7 +169,8 @@ class SaleOrder(models.Model): 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: {estimated_cost}") + self.message_post(body=f"Estimasi Ongkos Kirim: {self.delivery_amt}") + else: raise UserError("Gagal mendapatkan estimasi ongkir.") @@ -173,9 +183,9 @@ class SaleOrder(models.Model): data = { 'origin': int(origin_city_id), - 'originType': 'city', + 'originType': 'subdistrict', 'destination': int(destination_city_id), - 'destinationType': 'city', + 'destinationType': 'subdistrict', 'weight': int(total_weight * 1000), 'courier': courier, } @@ -186,16 +196,13 @@ class SaleOrder(models.Model): return None def _normalize_city_name(self, city_name): - # Ubah nama kota menjadi huruf kecil city_name = city_name.lower() - # Hilangkan prefiks "kabupaten" atau "kota" jika ada if city_name.startswith('kabupaten'): city_name = city_name.replace('kabupaten', '').strip() elif city_name.startswith('kota'): city_name = city_name.replace('kota', '').strip() - # Hilangkan spasi yang berlebihan city_name = " ".join(city_name.split()) return city_name @@ -206,7 +213,6 @@ class SaleOrder(models.Model): 'key': '7ac9883688da043b50cc32f0e3070bb6', } - # Normalisasi nama kota sebelum melakukan pencarian normalized_city_name = self._normalize_city_name(city_name) response = requests.get(url, headers=headers) -- cgit v1.2.3 From ac0f803ab0a68083ca0d1e540b5eb4c204c45245 Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Wed, 2 Oct 2024 14:59:44 +0700 Subject: disable create vendor and tax in sale order --- indoteknik_custom/views/sale_order.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index 17faaa95..029c99e9 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -116,7 +116,7 @@ - + - + -- cgit v1.2.3 From 27a603a8bc4750dcacd295e07144c5ce2621783c Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 2 Oct 2024 15:10:46 +0700 Subject: cr estimated shipping price + cr user company request --- indoteknik_api/controllers/api_v1/user.py | 2 +- indoteknik_custom/models/res_partner.py | 4 +-- indoteknik_custom/models/sale_order.py | 5 ++-- indoteknik_custom/models/user_company_request.py | 34 +++++++++++++++++++++++- indoteknik_custom/views/user_company_request.xml | 5 ++-- 5 files changed, 42 insertions(+), 8 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/user.py b/indoteknik_api/controllers/api_v1/user.py index 529873c0..52d773e2 100644 --- a/indoteknik_api/controllers/api_v1/user.py +++ b/indoteknik_api/controllers/api_v1/user.py @@ -163,7 +163,7 @@ class User(controller.Controller): query = """ SELECT name, levenshtein(name::text, %s) AS distance FROM res_partner - WHERE levenshtein(name::text, %s) < 3 + WHERE levenshtein(name::text, %s) < 3 ORDER BY distance ASC """ params = (business_name, business_name) diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index 2846c14b..25db16d0 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -18,8 +18,8 @@ class ResPartner(models.Model): ('pkp', 'PKP'), ('nonpkp', 'Non PKP') ]) - sppkp = fields.Char(string="SPPKP", tracking=3) - npwp = fields.Char(string="NPWP", tracking=3) + sppkp = fields.Char(string="SPPKP", tracking=True) + npwp = fields.Char(string="NPWP", tracking=True) counter = fields.Integer(string="Counter", default=0) leadtime = fields.Integer(string="Leadtime", default=0) digital_invoice_tax = fields.Boolean(string="Digital Invoice & Faktur Pajak") diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 8c506c47..2b20a533 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -145,8 +145,9 @@ class SaleOrder(models.Model): missing_weight_products.append(line.product_id.name) if missing_weight_products: - product_names = ', '.join(missing_weight_products) - self.message_post(body=f"Produk berikut tidak memiliki berat: {product_names}") + product_names = '\n'.join(missing_weight_products) + self.message_post(body=f"Produk berikut tidak memiliki berat: \n{product_names}") + if total_weight == 0: raise UserError("Tidak dapat mengestimasi ongkir tanpa berat yang valid.") diff --git a/indoteknik_custom/models/user_company_request.py b/indoteknik_custom/models/user_company_request.py index d540b0f6..1b3434b1 100644 --- a/indoteknik_custom/models/user_company_request.py +++ b/indoteknik_custom/models/user_company_request.py @@ -1,4 +1,4 @@ -from odoo import models, fields +from odoo import models, fields, api from odoo.exceptions import UserError from odoo.http import request @@ -13,6 +13,38 @@ class UserCompanyRequest(models.Model): ('approved', 'Approve'), ('rejected', 'Reject'), ], string='Approval') + similar_company_ids = fields.Many2many('res.partner', compute="_compute_similar_companies", string="Similar Companies") + + @api.depends('user_input') + def _compute_similar_companies(self): + for record in self: + if record.user_input: + record.similar_company_ids = [(6, 0, self.get_similar_companies(record.user_input))] + else: + record.similar_company_ids = [(6, 0, [])] + + def get_similar_companies(self, user_input): + query = """ + SELECT id + FROM res_partner + WHERE levenshtein(name::text, %s) < 3 + ORDER BY levenshtein(name::text, %s) ASC + """ + self.env.cr.execute(query, (user_input, user_input)) + return [row[0] for row in self.env.cr.fetchall()] + + def get_similar_companies(self, user_input): + query = """ + SELECT id + FROM res_partner + WHERE name ILIKE %s OR levenshtein(name::text, %s) < 3 + ORDER BY levenshtein(name::text, %s) ASC + """ + # Using '%' to match the partial company name + self.env.cr.execute(query, ('%' + user_input + '%', user_input, user_input)) + company_ids = [row[0] for row in self.env.cr.fetchall()] + return company_ids + def write(self, vals): user = self.get_user_by_email(self.user_id.email) diff --git a/indoteknik_custom/views/user_company_request.xml b/indoteknik_custom/views/user_company_request.xml index c9edd3f8..38340081 100644 --- a/indoteknik_custom/views/user_company_request.xml +++ b/indoteknik_custom/views/user_company_request.xml @@ -4,7 +4,7 @@ user.company.request.tree user.company.request - + @@ -28,7 +28,8 @@ - + + Date: Wed, 2 Oct 2024 15:31:02 +0700 Subject: add validation data realshippingaddress --- indoteknik_custom/models/sale_order.py | 19 +++++++++++++++++-- indoteknik_custom/views/res_partner.xml | 9 +++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 2b20a533..f8127d8c 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -419,6 +419,22 @@ class SaleOrder(models.Model): for line in order.order_line: total += line.vendor_subtotal order.purchase_total = total + + def check_data_real_delivery_address(self): + real_delivery_address = self.real_shipping_id + + if not real_delivery_address.state_id: + raise UserError('State Real Delivery Address harus diisi') + if not real_delivery_address.zip: + raise UserError('Zip code Real Delivery Address harus diisi') + if not real_delivery_address.mobile: + raise UserError('Mobile Real Delivery Address harus diisi') + if not real_delivery_address.phone: + raise UserError('Phone Real Delivery Address harus diisi') + if not real_delivery_address.kecamatan_id: + raise UserError('Kecamatan Real Delivery Address harus diisi') + if not real_delivery_address.kelurahan_id: + raise UserError('Kelurahan Real Delivery Address harus diisi') def generate_payment_link_midtrans_sales_order(self): # midtrans_url = 'https://app.sandbox.midtrans.com/snap/v1/transactions' # dev - sandbox @@ -565,8 +581,6 @@ class SaleOrder(models.Model): raise UserError('Phone Real Delivery Address harus diisi') if not real_delivery_address.kecamatan_id: raise UserError('Kecamatan Real Delivery Address harus diisi') - if not real_delivery_address.kelurahan_id: - raise UserError('Kelurahan Real Delivery Address harus diisi') @api.onchange('partner_id') def onchange_partner_contact(self): @@ -791,6 +805,7 @@ class SaleOrder(models.Model): def action_confirm(self): for order in self: + order.check_data_real_delivery_address() order.sale_order_check_approve() order._validate_order() order.order_line.validate_line() diff --git a/indoteknik_custom/views/res_partner.xml b/indoteknik_custom/views/res_partner.xml index d77e09b8..6d02a86b 100644 --- a/indoteknik_custom/views/res_partner.xml +++ b/indoteknik_custom/views/res_partner.xml @@ -29,6 +29,15 @@ 1 + + 0 + + + 0 + + + 0 + 1 -- cgit v1.2.3 From 16ddcb43b85c1deeda29bf6bb5db4ef73b8d20d2 Mon Sep 17 00:00:00 2001 From: stephanchrst Date: Wed, 2 Oct 2024 16:21:55 +0700 Subject: add suggested vendor by md, use later --- indoteknik_custom/models/sale_order_line.py | 2 ++ indoteknik_custom/views/sale_order.xml | 1 + 2 files changed, 3 insertions(+) diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py index d1dcd0af..14bb8c99 100644 --- a/indoteknik_custom/models/sale_order_line.py +++ b/indoteknik_custom/models/sale_order_line.py @@ -32,6 +32,7 @@ class SaleOrderLine(models.Model): reserved_from = fields.Char(string='Reserved From', copy=False) 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) @api.constrains('note_procurement') def note_procurement_to_apo(self): @@ -245,6 +246,7 @@ class SaleOrderLine(models.Model): # query, limit=1, order='count_trx_po desc, count_trx_po_vendor desc') price, taxes, vendor_id = self._get_purchase_price(line.product_id) line.vendor_id = vendor_id + line.md_vendor_id = vendor_id line.tax_id = line.order_id.sales_tax_id # price, taxes = line._get_valid_purchase_price(purchase_price) line.purchase_price = price diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index 029c99e9..1c75c96a 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -135,6 +135,7 @@ + -- cgit v1.2.3 From 799c216979fb4216422c2a584a763b286713b8d2 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 3 Oct 2024 10:04:18 +0700 Subject: add is in bu ke variant product solr --- indoteknik_custom/models/solr/product_product.py | 13 ++++++++++++- indoteknik_custom/models/solr/product_template.py | 3 ++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/models/solr/product_product.py b/indoteknik_custom/models/solr/product_product.py index 35e3a4b3..df20cc4b 100644 --- a/indoteknik_custom/models/solr/product_product.py +++ b/indoteknik_custom/models/solr/product_product.py @@ -47,6 +47,16 @@ class ProductProduct(models.Model): category_id = category.id category_name = category.name + # Check if the product's inventory location is in ID 57 or 83 + target_locations = [57, 83] + stock_quant = self.env['stock.quant'].search([ + ('product_id', 'in', variant.product_variant_ids.ids), + ('location_id', 'in', target_locations), + ('quantity', '>', 0) + ]) + + is_in_bu = bool(stock_quant) + document = solr_model.get_doc('variants', variant.id) document.update({ @@ -76,7 +86,8 @@ class ProductProduct(models.Model): 'publish_b': not variant.unpublished, 'sni_b': variant.sni, 'tkdn_b': variant.tkdn, - 'qty_sold_f': variant.qty_sold + 'qty_sold_f': variant.qty_sold, + "is_in_bu_b": is_in_bu, }) self.solr().add(docs=[document], softCommit=True) diff --git a/indoteknik_custom/models/solr/product_template.py b/indoteknik_custom/models/solr/product_template.py index 1eb6f31b..70246ba3 100644 --- a/indoteknik_custom/models/solr/product_template.py +++ b/indoteknik_custom/models/solr/product_template.py @@ -74,7 +74,8 @@ class ProductTemplate(models.Model): target_locations = [57, 83] stock_quant = self.env['stock.quant'].search([ ('product_id', 'in', template.product_variant_ids.ids), - ('location_id', 'in', target_locations) + ('location_id', 'in', target_locations), + ('quantity', '>', 0) ]) is_in_bu = bool(stock_quant) -- cgit v1.2.3 From 4a69c71eab2d4ea3504a0cf6e3a9ca241be48594 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 3 Oct 2024 10:12:24 +0700 Subject: push --- indoteknik_custom/models/sale_order.py | 32 ++++++++++++++++++++--- indoteknik_custom/models/sale_order_line.py | 7 ++++++ indoteknik_custom/models/vendor_approval.py | 39 +++++++++++++++++++++++++++++ indoteknik_custom/views/sale_order.xml | 2 ++ 4 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 indoteknik_custom/models/vendor_approval.py diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index f8127d8c..538e0209 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -145,9 +145,8 @@ class SaleOrder(models.Model): 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 = '
'.join(missing_weight_products) + self.message_post(body=f"Produk berikut tidak memiliki berat:
{product_names}") if total_weight == 0: raise UserError("Tidak dapat mengestimasi ongkir tanpa berat yang valid.") @@ -802,9 +801,36 @@ 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.id != l.vendor_md_id.id) + if different_vendor: + vendor_approval = self.env['vendor.approval'].create({ + 'order_id': self.id, + 'state': 'draft', + }) + for line in self.line: + 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, + }) + + return True + else: + return False + + def action_confirm(self): for order in self: + if order.validate_partner_invoice_due(): + 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 d1dcd0af..dca8534c 100644 --- a/indoteknik_custom/models/sale_order_line.py +++ b/indoteknik_custom/models/sale_order_line.py @@ -14,7 +14,12 @@ 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', readonly=True, + change_default=True, index=True, tracking=1 + ) purchase_price = fields.Float('Purchase', required=True, digits='Product Price', default=0.0) + purchase_price_md = fields.Float('MD 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) @@ -245,9 +250,11 @@ class SaleOrderLine(models.Model): # query, limit=1, order='count_trx_po desc, count_trx_po_vendor desc') price, taxes, vendor_id = self._get_purchase_price(line.product_id) line.vendor_id = vendor_id + line.vendor_md_id = vendor_id line.tax_id = line.order_id.sales_tax_id # price, taxes = line._get_valid_purchase_price(purchase_price) line.purchase_price = price + line.purchase_price_md = price line.purchase_tax_id = taxes attribute_values = line.product_id.product_template_attribute_value_ids.mapped('name') diff --git a/indoteknik_custom/models/vendor_approval.py b/indoteknik_custom/models/vendor_approval.py new file mode 100644 index 00000000..4369a193 --- /dev/null +++ b/indoteknik_custom/models/vendor_approval.py @@ -0,0 +1,39 @@ +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) + + 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') + purchase_price = fields.Many2one(string='Purchase Price') + purchase_price_md= fields.Many2one(string='Purchase Price MD') + + diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index 029c99e9..f2a57fc7 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -117,6 +117,7 @@
+ + -- cgit v1.2.3 From 7059f095dd09649f7c12f74ff29200f644bbc854 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 3 Oct 2024 11:55:16 +0700 Subject: push --- indoteknik_custom/__manifest__.py | 1 + indoteknik_custom/models/__init__.py | 1 + indoteknik_custom/models/sale_order.py | 12 ++-- indoteknik_custom/models/sale_order_line.py | 10 +-- indoteknik_custom/models/vendor_approval.py | 14 ++++ indoteknik_custom/security/ir.model.access.csv | 2 + indoteknik_custom/views/vendor_approval.xml | 98 ++++++++++++++++++++++++++ 7 files changed, 129 insertions(+), 9 deletions(-) create mode 100644 indoteknik_custom/views/vendor_approval.xml diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index e1a67592..116c64ec 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -143,6 +143,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 3d700ce0..a0064c06 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -128,3 +128,4 @@ from . import sales_order_reject from . import approval_date_doc from . import account_tax from . import approval_unreserve +from . import vendor_approval diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 538e0209..a6ea6c81 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) @@ -703,6 +705,8 @@ class SaleOrder(models.Model): self._validate_order() for order in self: order.order_line.validate_line() + if order.validate_different_vendor() and not self.vendor_approval: + return self._create_notification_action('Notification', 'Terdapat Vendor yang berbeda dengan MD Vendor') partner = order.partner_id.parent_id or order.partner_id if not partner.property_payment_term_id: @@ -803,12 +807,14 @@ class SaleOrder(models.Model): }).send() def validate_different_vendor(self): - different_vendor = self.order_line.filtered(lambda l: l.vendor_id.id != l.vendor_md_id.id) + 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 different_vendor: vendor_approval = self.env['vendor.approval'].create({ 'order_id': self.id, 'state': 'draft', }) + + self.vendor_approval_id = vendor_approval.id for line in self.line: self.env['vendor.approval.line'].create({ 'vendor_approval_id': vendor_approval.id, @@ -823,12 +829,10 @@ class SaleOrder(models.Model): return True else: return False - - def action_confirm(self): for order in self: - if order.validate_partner_invoice_due(): + if order.validate_different_vendor() and not self.vendor_approval: return self._create_notification_action('Notification', 'Terdapat Vendor yang berbeda dengan MD Vendor') order.check_data_real_delivery_address() diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py index dca8534c..06b0f53d 100644 --- a/indoteknik_custom/models/sale_order_line.py +++ b/indoteknik_custom/models/sale_order_line.py @@ -14,12 +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', readonly=True, - change_default=True, index=True, tracking=1 - ) + 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', 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) @@ -138,6 +135,7 @@ class SaleOrderLine(models.Model): elif self.product_id.categ_id.id == 34: # finish good / manufacturing only cost = self.product_id.standard_price self.purchase_price = cost + self.purchase_price_md = cost elif self.product_id.x_manufacture.override_vendor_id: # purchase_price = self.env['purchase.pricelist'].search( # [('vendor_id', '=', self.product_id.x_manufacture.override_vendor_id.id), @@ -145,6 +143,8 @@ class SaleOrderLine(models.Model): # limit=1, order='count_trx_po desc, count_trx_po_vendor desc') price, taxes, vendor_id = self._get_purchase_price_by_vendor(self.product_id, self.vendor_id) self.purchase_price = price + self.purchase_price_md = price + self.vendor_md_id = self.vendor_id self.purchase_tax_id = taxes # else: # purchase_price = self.env['purchase.pricelist'].search( diff --git a/indoteknik_custom/models/vendor_approval.py b/indoteknik_custom/models/vendor_approval.py index 4369a193..442aec7a 100644 --- a/indoteknik_custom/models/vendor_approval.py +++ b/indoteknik_custom/models/vendor_approval.py @@ -15,7 +15,21 @@ class VendorApproval(models.Model): 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', 'Cancel')], string='State', tracking=True) + + 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() + + 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' + def unlink(self): res = super(VendorApproval, self).unlink() if not self._name == 'vendor.approval': diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index 19e3bdca..1cd7c1e9 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -138,3 +138,5 @@ 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 diff --git a/indoteknik_custom/views/vendor_approval.xml b/indoteknik_custom/views/vendor_approval.xml new file mode 100644 index 00000000..c3f5ee77 --- /dev/null +++ b/indoteknik_custom/views/vendor_approval.xml @@ -0,0 +1,98 @@ + + + + + vendor.approval.tree + vendor.approval + + + + + + + + + + + + vendor.approval.line.tree + vendor.approval.line + + + + + + + + + + + + + + vendor.approval.form + vendor.approval + +
+
+
+ + + + + + + + + + + + + + +
+ + +
+
+
+
+ + + vendor.approval.search.view + vendor.approval + + + + + + + + + + + Vendor Approval + ir.actions.act_window + vendor.approval + tree,form + + + +
+
-- cgit v1.2.3 From 48c220d1c38f435962b8b630c5209ba781ca30df Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Fri, 4 Oct 2024 14:23:59 +0700 Subject: push --- indoteknik_custom/models/sale_order.py | 71 ++++++++++++++++++++------- indoteknik_custom/models/sale_order_line.py | 8 +-- indoteknik_custom/models/vendor_approval.py | 28 +++++++++-- indoteknik_custom/models/website_user_cart.py | 2 +- indoteknik_custom/views/ir_sequence.xml | 10 ++++ indoteknik_custom/views/sale_order.xml | 9 ++-- indoteknik_custom/views/vendor_approval.xml | 16 ++++-- 7 files changed, 110 insertions(+), 34 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index a6ea6c81..0f22a7d4 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -135,6 +135,12 @@ 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) + total_weight = fields.Float(string='Total Weight', compute='_compute_total_weight') + + def _compute_total_weight(self): + for order in self: + order.total_weight = sum(line.weight for line in order.order_line) + def action_estimate_shipping(self): total_weight = 0 missing_weight_products = [] @@ -155,38 +161,45 @@ class SaleOrder(models.Model): # 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 + destination_subsdistrict_id = self.real_shipping_id.kecamatan_id.rajaongkir_id - 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) - - 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 = "
".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}
Service: {service_ongkir}
Description: {description_ongkir}
ETD: {etd_ongkir}
Detail Lain:
{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, @@ -212,7 +225,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) @@ -228,14 +241,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 @@ -808,14 +824,24 @@ class SaleOrder(models.Model): 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 self.line: + + for line in different_vendor: self.env['vendor.approval.line'].create({ 'vendor_approval_id': vendor_approval.id, 'product_id': line.product_id.id, @@ -824,11 +850,18 @@ class SaleOrder(models.Model): '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: diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py index 06b0f53d..98a6c759 100644 --- a/indoteknik_custom/models/sale_order_line.py +++ b/indoteknik_custom/models/sale_order_line.py @@ -34,6 +34,7 @@ class SaleOrderLine(models.Model): reserved_from = fields.Char(string='Reserved From', copy=False) item_percent_margin_without_deduction = fields.Float('%Margin', compute='_compute_item_margin_without_deduction') weight = fields.Float(string='Weight') + margin_md = fields.Float(string='Margin MD') @api.constrains('note_procurement') def note_procurement_to_apo(self): @@ -127,6 +128,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 @@ -135,7 +139,6 @@ class SaleOrderLine(models.Model): elif self.product_id.categ_id.id == 34: # finish good / manufacturing only cost = self.product_id.standard_price self.purchase_price = cost - self.purchase_price_md = cost elif self.product_id.x_manufacture.override_vendor_id: # purchase_price = self.env['purchase.pricelist'].search( # [('vendor_id', '=', self.product_id.x_manufacture.override_vendor_id.id), @@ -143,8 +146,6 @@ class SaleOrderLine(models.Model): # limit=1, order='count_trx_po desc, count_trx_po_vendor desc') price, taxes, vendor_id = self._get_purchase_price_by_vendor(self.product_id, self.vendor_id) self.purchase_price = price - self.purchase_price_md = price - self.vendor_md_id = self.vendor_id self.purchase_tax_id = taxes # else: # purchase_price = self.env['purchase.pricelist'].search( @@ -251,6 +252,7 @@ class SaleOrderLine(models.Model): price, taxes, vendor_id = self._get_purchase_price(line.product_id) line.vendor_id = vendor_id line.vendor_md_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 = price diff --git a/indoteknik_custom/models/vendor_approval.py b/indoteknik_custom/models/vendor_approval.py index 442aec7a..e540b8fc 100644 --- a/indoteknik_custom/models/vendor_approval.py +++ b/indoteknik_custom/models/vendor_approval.py @@ -15,21 +15,35 @@ class VendorApproval(models.Model): 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', 'Cancel')], string='State', tracking=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': @@ -47,7 +61,13 @@ class VendorApprovalLine(models.Model): 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') - purchase_price = fields.Many2one(string='Purchase Price') - purchase_price_md= fields.Many2one(string='Purchase Price 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..8a321187 100644 --- a/indoteknik_custom/models/website_user_cart.py +++ b/indoteknik_custom/models/website_user_cart.py @@ -173,7 +173,7 @@ class WebsiteUserCart(models.Model): } return result - def action_mail_reminder_to_checkout(self, limit=10): + def action_mail_reminder_to_checkout(self, limit=250): user_ids = self.search([]).mapped('user_id')[:limit] for user in user_ids: 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 @@ 1 + + Vendor Approval + vendor.approval + TRUE + VA/%(year)s/ + 5 + 1 + 1 + + Approval Unreserve approval.unreserve diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index f2a57fc7..9a637441 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -72,6 +72,7 @@ + @@ -80,6 +81,7 @@ +