diff options
| author | Azka Nathan <darizkyfaz@gmail.com> | 2024-07-02 10:06:29 +0700 |
|---|---|---|
| committer | Azka Nathan <darizkyfaz@gmail.com> | 2024-07-02 10:06:29 +0700 |
| commit | a10024fec206f68791c87a5a4e56e4c6bce28f5c (patch) | |
| tree | 627d0ca2443eecbcb44b1b4eeda98f94c9ea3496 /indoteknik_custom/models | |
| parent | 51c19eca13239fe20ae592f8e9ee0d23f8904c5f (diff) | |
| parent | f9c5b3dffcd71bfa9dea74c946d7b4277db66bd6 (diff) | |
Merge branch 'production' into feature/add_voucher_pastihemat_productsolr
Diffstat (limited to 'indoteknik_custom/models')
| -rwxr-xr-x | indoteknik_custom/models/__init__.py | 3 | ||||
| -rw-r--r-- | indoteknik_custom/models/account_move.py | 28 | ||||
| -rw-r--r-- | indoteknik_custom/models/account_move_due_extension.py | 1 | ||||
| -rw-r--r-- | indoteknik_custom/models/automatic_purchase.py | 12 | ||||
| -rw-r--r-- | indoteknik_custom/models/delivery_carrier.py | 1 | ||||
| -rw-r--r-- | indoteknik_custom/models/logbook_bill.py | 103 | ||||
| -rw-r--r-- | indoteknik_custom/models/manufacturing.py | 4 | ||||
| -rwxr-xr-x | indoteknik_custom/models/purchase_order.py | 117 | ||||
| -rw-r--r-- | indoteknik_custom/models/purchasing_job.py | 45 | ||||
| -rw-r--r-- | indoteknik_custom/models/purchasing_job_multi_update.py | 2 | ||||
| -rw-r--r-- | indoteknik_custom/models/report_logbook_bill.py | 98 | ||||
| -rw-r--r-- | indoteknik_custom/models/report_stock_forecasted.py | 35 | ||||
| -rwxr-xr-x | indoteknik_custom/models/sale_order.py | 108 | ||||
| -rw-r--r-- | indoteknik_custom/models/sale_order_line.py | 16 | ||||
| -rw-r--r-- | indoteknik_custom/models/sale_order_multi_uangmuka_penjualan.py | 136 | ||||
| -rw-r--r-- | indoteknik_custom/models/solr/apache_solr.py | 39 |
16 files changed, 613 insertions, 135 deletions
diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index e6fefe2a..ffd1b645 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -117,3 +117,6 @@ from . import ged from . import account_move_multi_update_bills from . import def_cargo from . import purchase_order_multi_uangmuka2 +from . import logbook_bill +from . import report_logbook_bill +from . import sale_order_multi_uangmuka_penjualan diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 0ba25ad8..fbadd2aa 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -64,6 +64,34 @@ class AccountMove(models.Model): nomor_kwitansi = fields.Char(string="Nomor Kwitansi") @api.model + def generate_attachment(self, record): + # Fetch the binary field + file_content = record.efaktur_document + file_name = "efaktur_document_{}.pdf".format(record.id) # Adjust the file extension if necessary + + attachment = self.env['ir.attachment'].create({ + 'name': file_name, + 'type': 'binary', + 'datas': file_content, + 'res_model': record._name, + 'res_id': record.id, + }) + return attachment + + # @api.constrains('efaktur_document') + def send_scheduled_email(self): + # Get the records for which emails need to be sent + records = self.search([('id', 'in', self.ids)]) + template = self.env.ref('indoteknik_custom.mail_template_efaktur_document') + + for record in records: + attachment = self.generate_attachment(record) + email_values = { + 'attachment_ids': [(4, attachment.id)] + } + template.send_mail(record.id, email_values=email_values, force_send=True) + + @api.model def create(self, vals): vals['nomor_kwitansi'] = self.env['ir.sequence'].next_by_code('nomor.kwitansi') or '0' result = super(AccountMove, self).create(vals) diff --git a/indoteknik_custom/models/account_move_due_extension.py b/indoteknik_custom/models/account_move_due_extension.py index cac73d07..c9af7f8d 100644 --- a/indoteknik_custom/models/account_move_due_extension.py +++ b/indoteknik_custom/models/account_move_due_extension.py @@ -14,6 +14,7 @@ class DueExtension(models.Model): 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) + invoice_id = fields.Many2one('account.move', related='due_line.invoice_id', string='Invoice', readonly=False) due_line = fields.One2many('due.extension.line', 'due_id', string='Due Extension Lines', auto_join=True) old_due = fields.Date(string="Old Due") description = fields.Text(string="Description") diff --git a/indoteknik_custom/models/automatic_purchase.py b/indoteknik_custom/models/automatic_purchase.py index 2c83c0ea..73416c48 100644 --- a/indoteknik_custom/models/automatic_purchase.py +++ b/indoteknik_custom/models/automatic_purchase.py @@ -506,13 +506,15 @@ class AutomaticPurchase(models.Model): taxes = '' human_last_update = purchase_price.human_last_update or datetime.min system_last_update = purchase_price.system_last_update or datetime.min - - price = purchase_price.product_price - taxes = purchase_price.taxes_product_id.id + + if purchase_price.taxes_product_id.type_tax_use == 'purchase': + price = purchase_price.product_price + taxes = purchase_price.taxes_product_id.id if system_last_update > human_last_update: - price = purchase_price.system_price - taxes = purchase_price.taxes_system_id.id + if purchase_price.taxes_system_id.type_tax_use == 'purchase': + price = purchase_price.system_price + taxes = purchase_price.taxes_system_id.id return price, taxes diff --git a/indoteknik_custom/models/delivery_carrier.py b/indoteknik_custom/models/delivery_carrier.py index b3ae44e9..0bf1f08d 100644 --- a/indoteknik_custom/models/delivery_carrier.py +++ b/indoteknik_custom/models/delivery_carrier.py @@ -6,3 +6,4 @@ class DeliveryCarrier(models.Model): pic_name = fields.Char(string='PIC Name') pic_phone = fields.Char(string='PIC Phone') + address = fields.Char(string='Address') diff --git a/indoteknik_custom/models/logbook_bill.py b/indoteknik_custom/models/logbook_bill.py new file mode 100644 index 00000000..578ad59b --- /dev/null +++ b/indoteknik_custom/models/logbook_bill.py @@ -0,0 +1,103 @@ +from odoo import models, fields, api, _ +from odoo.exceptions import UserError +from pytz import timezone +from datetime import datetime + +class LogbookBill(models.TransientModel): + _name = 'logbook.bill' + + name = fields.Char(string='Name', default='Logbook Bill') + logbook_bill_line = fields.One2many( + comodel_name='logbook.bill.line', + inverse_name='logbook_bill_id', + string='Logbook Bill Line' + ) + + + def grand_total(self, picking): + total = 0 + for line in picking.move_ids_without_package: + po = self.env['purchase.order.line'].search([ + ('order_id', '=', picking.purchase_id.id), + ('product_id', '=', line.product_id.id), + ], order='id desc', limit=1) + total += line.quantity_done * po.price_unit + return total + + def create_logbook_bill(self): + logbook_line = self.logbook_bill_line + + current_time = datetime.utcnow() + report_logbook_ids = [] + parameters_header = { + 'date': current_time, + 'created_by': self.env.user.id, + } + + report_logbook = self.env['report.logbook.bill'].create([parameters_header]) + for line in logbook_line: + picking = self.env['stock.picking'].search([('name', '=', line.name)], limit=1) + stock = picking + parent_id = stock.partner_id.parent_id.id + parent_id = parent_id if parent_id else stock.partner_id.id + + data = { + 'purchase_id': stock.purchase_id.id, + 'name': stock.name, + 'grand_total': self.grand_total(picking), + 'partner_id': parent_id, + 'invoice': line.invoice, + 'surat_jalan': line.surat_jalan, + 'proforma_invoice': line.proforma_invoice, + 'faktur_pajak': line.faktur_pajak, + 'date_approve': stock.date_done, + 'report_logbook_bill_id': report_logbook.id, + 'note': line.note, + 'note_finance': line.note_finance + } + self.env['report.logbook.bill.line'].create([data]) + + report_logbook_ids.append(report_logbook.id) + line.unlink() + + self.unlink() + return { + 'name': _('Report Logbook Bill'), + 'view_mode': 'tree,form', + 'res_model': 'report.logbook.bill', + 'target': 'current', + 'type': 'ir.actions.act_window', + 'domain': [('id', 'in', report_logbook_ids)], + } + +class LogbookBillLine(models.TransientModel): + _name = 'logbook.bill.line' + + name = fields.Char(string='Name') + logbook_bill_id = fields.Many2one('logbook.bill', string='Logbook Bill') + partner_id = fields.Many2one('res.partner', string='Customer') + purchase_id = fields.Many2one('purchase.order', string='Purchase Order') + invoice = fields.Boolean(string='Invoice') + faktur_pajak = fields.Boolean(string='Faktur Pajak') + surat_jalan = fields.Boolean(string='Surat Jalan') + proforma_invoice = fields.Boolean(string='Proforma Invoice') + date_approve = fields.Datetime(string='Date Approve', tracking=3) + note = fields.Char(string='Note Logistik') + note_finance = fields.Char(string='Note Finance') + + @api.onchange('name') + def onchange_name(self): + current_time = datetime.now(timezone('Asia/Jakarta')).strftime('%Y-%m-%d %H:%M:%S') + + if self.name: + if len(self.name) == 13: + self.name = self.name[:-1] + picking = self.env['stock.picking'].search([('name', '=', self.name)], limit=1) + if picking: + self.partner_id = picking.partner_id + self.purchase_id = picking.purchase_id.id + + self.date_approve = picking.date_done + + else: + raise UserError('Nomor DO tidak ditemukan') diff --git a/indoteknik_custom/models/manufacturing.py b/indoteknik_custom/models/manufacturing.py index 2455a117..37c4e909 100644 --- a/indoteknik_custom/models/manufacturing.py +++ b/indoteknik_custom/models/manufacturing.py @@ -23,7 +23,9 @@ class Manufacturing(models.Model): def button_mark_done(self): if self._name != 'mrp.production': return super(Manufacturing, self).button_mark_done() - + # Check product category + if self.product_id.categ_id.name != 'Finish Good': + raise UserError('Tidak bisa di complete karna product category bukan Unit / Finish Good') for line in self.move_raw_ids: # if line.quantity_done > 0 and line.quantity_done != self.product_uom_qty: # raise UserError('Qty Consume per Line tidak sama dengan Qty to Produce') diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py index 7b0fa20c..4a029441 100755 --- a/indoteknik_custom/models/purchase_order.py +++ b/indoteknik_custom/models/purchase_order.py @@ -60,6 +60,60 @@ class PurchaseOrder(models.Model): matches_so = fields.Many2many('sale.order', string='Matches SO', compute='_compute_matches_so') is_create_uangmuka = fields.Boolean(string='Uang Muka?') move_id = fields.Many2one('account.move', string='Account Move') + logbook_bill_id = fields.Many2one('report.logbook.bill', string='Logbook Bill') + status_printed = fields.Selection([ + ('not_printed', 'Belum Print'), + ('printed', 'Printed') + ], string='Printed?', copy=False, tracking=True) + + def _prepare_invoice(self): + """Prepare the dict of values to create the new invoice for a purchase order. + """ + self.ensure_one() + move_type = self._context.get('default_move_type', 'in_invoice') + journal = self.env['account.move'].with_context(default_move_type=move_type)._get_default_journal() + if not journal: + raise UserError(_('Please define an accounting purchase journal for the company %s (%s).') % (self.company_id.name, self.company_id.id)) + + stock_picking = self.env['stock.picking'].search([ + ('purchase_id', '=', self.id), + ('state', '=', 'done') + ], order='date_done desc', limit=1) + + date_done = stock_picking.date_done + + day_extension = int(self.payment_term_id.line_ids.days) + payment_schedule = date_done + timedelta(days=day_extension) + + if payment_schedule.weekday() == 0: + payment_schedule -= timedelta(days=4) + elif payment_schedule.weekday() == 2: + payment_schedule -= timedelta(days=1) + elif payment_schedule.weekday() == 4: + payment_schedule -= timedelta(days=1) + elif payment_schedule.weekday() == 5: + payment_schedule -= timedelta(days=2) + elif payment_schedule.weekday() == 6: + payment_schedule -= timedelta(days=3) + + partner_invoice_id = self.partner_id.address_get(['invoice'])['invoice'] + invoice_vals = { + 'ref': self.partner_ref or '', + 'move_type': move_type, + 'narration': self.notes, + 'currency_id': self.currency_id.id, + 'invoice_user_id': self.user_id and self.user_id.id or self.env.user.id, + 'partner_id': partner_invoice_id, + 'fiscal_position_id': (self.fiscal_position_id or self.fiscal_position_id.get_fiscal_position(partner_invoice_id)).id, + 'payment_reference': self.partner_ref or '', + 'partner_bank_id': self.partner_id.bank_ids[:1].id, + 'invoice_origin': self.name, + 'invoice_payment_term_id': self.payment_term_id.id, + 'invoice_line_ids': [], + 'company_id': self.company_id.id, + 'payment_schedule': payment_schedule + } + return invoice_vals def _compute_matches_so(self): for po in self: @@ -389,6 +443,12 @@ 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") + # Validasi pajak + if not line.taxes_id: + raise UserError("Masukkan Tax untuk produk") + for tax in line.taxes_id: + if tax.type_tax_use != 'purchase': + raise UserError("Pastikan Tax Category nya adalah Purchase pada produk %s" % line.product_id.name) if line.price_unit != line.price_vendor and line.price_vendor != 0: self._send_po_not_sync() send_email = True @@ -589,63 +649,6 @@ class PurchaseOrder(models.Model): self.total_so_margin = 0 self.total_so_percent_margin = 0 - # def compute_total_margin_from_apo(self): - # purchase_price_dict = {} - - - # for line in self.order_sales_match_line: - # for lines in self.order_line: - # product_id = lines.product_id.id - - # if product_id not in purchase_price_dict: - # purchase_price_dict[product_id] = lines.price_subtotal - - # sum_so_margin = sum_sales_price = sum_margin = 0 - # sale_order_line = line.sale_line_id - - # if not sale_order_line: - # sale_order_line = self.env['sale.order.line'].search([ - # ('product_id', '=', line.product_id.id), - # ('order_id', '=', line.sale_id.id) - # ], limit=1, order='price_reduce_taxexcl') - - # sum_so_margin += sale_order_line.item_margin - - # # sales_price = sale_order_line.price_reduce_taxexcl * line.qty_so - # sales_price = sale_order_line.price_reduce_taxexcl * lines.product_qty - - # if sale_order_line.order_id.shipping_cost_covered == 'indoteknik': - # sales_price -= sale_order_line.delivery_amt_line - - # if sale_order_line.order_id.fee_third_party > 0: - # sales_price -= sale_order_line.fee_third_party_line - - # sum_sales_price += sales_price - - # product_id = sale_order_line.product_id.id - - # purchase_price = purchase_price_dict.get(product_id, 0) - # # purchase_price = lines.price_subtotal - # if lines.order_id.delivery_amount > 0: - # purchase_price += lines.delivery_amt_line - - # if line.purchase_order_id.delivery_amount > 0: - # purchase_price += line.delivery_amt_line - - # real_item_margin = sales_price - purchase_price - # sum_margin += real_item_margin - - # if sum_so_margin != 0 and sum_sales_price != 0 and sum_margin != 0: - # self.total_so_margin = sum_so_margin - # self.total_so_percent_margin = round((sum_so_margin / sum_sales_price), 2) * 100 - # self.total_margin = sum_margin - # self.total_percent_margin = round((sum_margin / sum_sales_price), 2) * 100 - # else: - # self.total_margin = 0 - # self.total_percent_margin = 0 - # self.total_so_margin = 0 - # self.total_so_percent_margin = 0 - def compute_amt_total_without_service(self): for order in self: sum_price_total = 0 diff --git a/indoteknik_custom/models/purchasing_job.py b/indoteknik_custom/models/purchasing_job.py index 373e469a..40061d22 100644 --- a/indoteknik_custom/models/purchasing_job.py +++ b/indoteknik_custom/models/purchasing_job.py @@ -41,14 +41,13 @@ class PurchasingJob(models.Model): } def init(self): - tools.drop_view_if_exists(self.env.cr, self._table) - self.env.cr.execute(""" + query = """ CREATE OR REPLACE VIEW %s AS ( WITH latest_purchase_orders AS ( SELECT pol.product_id, po.user_id, - ROW_NUMBER() OVER (PARTITION BY po.partner_id ORDER BY po.create_date DESC) AS order_rank + ROW_NUMBER() OVER (PARTITION BY pol.product_id ORDER BY po.create_date DESC) AS order_rank FROM purchase_order po RIGHT JOIN purchase_order_line pol ON pol.order_id = po.id LEFT JOIN res_partner rp ON rp.id = po.partner_id @@ -70,11 +69,26 @@ class PurchasingJob(models.Model): LEFT JOIN sale_order_line sol ON sol.id = vso.sale_line_id ) AS sub WHERE sub.vendor_id IS NOT NULL + ), + unique_sub AS ( + SELECT DISTINCT + vso.product_id, + sol.vendor_id + FROM v_sales_outstanding vso + LEFT JOIN sale_order_line sol ON sol.id = vso.sale_line_id + WHERE sol.vendor_id IS NOT NULL + ), + latest_po_filtered AS ( + SELECT + product_id, + user_id + FROM latest_purchase_orders + WHERE order_rank = 1 ) SELECT pmp.product_id AS id, pmp.product_id, - sub.vendor_id, + MAX(sub.vendor_id) AS vendor_id, pmp.brand, pmp.item_code, pmp.product, @@ -84,30 +98,23 @@ class PurchasingJob(models.Model): pmp.action, MAX(pjs.status_apo) AS status_apo, MAX(pjs.note) AS note, - ru.user_id AS purchase_representative_id + MAX(ru.user_id) AS purchase_representative_id FROM v_procurement_monitoring_by_product pmp LEFT JOIN purchasing_job_state pjs ON pjs.purchasing_job_id = pmp.product_id - LEFT JOIN ( - SELECT - vso.product_id, - sol.vendor_id - FROM v_sales_outstanding vso - LEFT JOIN sale_order_line sol ON sol.id = vso.sale_line_id - ) AS sub ON sub.product_id = pmp.product_id - LEFT JOIN latest_purchase_orders po ON po.product_id = pmp.product_id + LEFT JOIN unique_sub sub ON sub.product_id = pmp.product_id + LEFT JOIN latest_po_filtered po ON po.product_id = pmp.product_id LEFT JOIN random_user_ids ru ON ru.vendor_id = sub.vendor_id OR (ru.vendor_id IS NULL AND sub.vendor_id != 9688) WHERE pmp.action = 'kurang' - AND sub.vendor_id IS NOT NULL + AND sub.vendor_id IS NOT NULL GROUP BY pmp.product_id, pmp.brand, pmp.item_code, pmp.product, - pmp.action, - sub.vendor_id, - ru.user_id - ) - """ % self._table) + pmp.action + )""" % self._table + + self.env.cr.execute(query) def open_form_multi_generate_request_po(self): diff --git a/indoteknik_custom/models/purchasing_job_multi_update.py b/indoteknik_custom/models/purchasing_job_multi_update.py index deba960a..80a43e45 100644 --- a/indoteknik_custom/models/purchasing_job_multi_update.py +++ b/indoteknik_custom/models/purchasing_job_multi_update.py @@ -18,7 +18,7 @@ class PurchasingJobMultiUpdate(models.TransientModel): ('purchasing_job_id', '=', product.id) ]) - purchasing_job_state.unlink() + # purchasing_job_state.unlink() purchasing_job_state.create({ 'purchasing_job_id': product.id, diff --git a/indoteknik_custom/models/report_logbook_bill.py b/indoteknik_custom/models/report_logbook_bill.py new file mode 100644 index 00000000..9a7c1535 --- /dev/null +++ b/indoteknik_custom/models/report_logbook_bill.py @@ -0,0 +1,98 @@ +from odoo import models, fields, api +from odoo.exceptions import UserError +from pytz import timezone +from datetime import datetime + +class ReportLogbookBill(models.Model): + _name = 'report.logbook.bill' + _description = "Logbook Bill" + _inherit = ['mail.thread'] + _rec_name = 'name' + + name = fields.Char(string='Name', default='Logbook Bill') + date = fields.Datetime(string='Date Created') + date_approve = fields.Datetime(string='Date Approve', tracking=3) + date_pengajuan = fields.Datetime(string='Date Pengajuan', tracking=3) + approve_by_finance = fields.Boolean(string='Approve By Finance', tracking=3) + pengajuan_by = fields.Many2one(comodel_name='res.users', string='Pengajuan By', tracking=3) + approve_by = fields.Many2one(comodel_name='res.users', string='Approve By', tracking=3) + created_by = fields.Many2one(comodel_name='res.users', string='Created By', tracking=3) + report_logbook_bill_line = fields.One2many( + comodel_name='report.logbook.bill.line', + inverse_name='report_logbook_bill_id', + string='Logbook Bill Line' + ) + state = fields.Selection( + [('belum_terima', 'Belum Terima'), + ('terima_sebagian', 'Terima Sebagian'), + ('terima_semua', 'Sudah di terima semua'), + ], + default='terima_semua', + string='Status', + tracking=True, + ) + + state_pengajuan = fields.Selection( + [('pengajuan', 'Pengajuan'), + ('diajukan', 'Sudah Diajukan'), + ], + default='pengajuan', + string='Status Pengajuan', + tracking=True, + ) + + count_line = fields.Char(string='Count Line', compute='_compute_count_line') + + @api.depends('report_logbook_bill_line') + def _compute_count_line(self): + for rec in self: + rec.count_line = len(rec.report_logbook_bill_line) + + @api.model + def create(self, vals): + vals['name'] = self.env['ir.sequence'].next_by_code('report.logbook.bill') or '0' + result = super(ReportLogbookBill, self).create(vals) + return result + + def approve(self): + current_time = datetime.utcnow() + if self.env.user.is_accounting: + self.approve_by_finance = True + self.date_approve = current_time + self.approve_by = self.env.user.id + if any(line.not_exist for line in self.report_logbook_bill_line): + if all(line.not_exist for line in self.report_logbook_bill_line): + self.state = 'belum_terima' + else: + self.state = 'terima_sebagian' + else: + self.state = 'terima_semua' + else: + if self.env.user.is_logistic_approver: + self.state_pengajuan = 'diajukan' + self.date_pengajuan = current_time + self.pengajuan_by = self.env.user.id + self.relation_po_to_logbook() + + def relation_po_to_logbook(self): + for line in self.report_logbook_bill_line: + line.purchase_id.logbook_bill_id = self.id + +class ReportLogbookBillLine(models.Model): + _name = 'report.logbook.bill.line' + + name = fields.Char(string='Name') + logbook_bill_id = fields.Many2one('report.logbook.bill', string='Logbook Bill') + purchase_id = fields.Many2one('purchase.order', string='Purchase Order') + invoice = fields.Boolean(string='Invoice') + faktur_pajak = fields.Boolean(string='FP') + surat_jalan = fields.Boolean(string='SJ') + purchase_id = fields.Many2one('purchase.order', string='Purchase Order') + partner_id = fields.Many2one('res.partner', string='Customer') + proforma_invoice = fields.Boolean(string='Proforma Inv') + report_logbook_bill_id = fields.Many2one('report.logbook.bill', string='Logbook Bill') + not_exist = fields.Boolean(string='Not Exist') + date_approve = fields.Datetime(string='Date Approve', tracking=3) + grand_total = fields.Float(string='Grand Total') + note = fields.Char(string='Note Logistik') + note_finance = fields.Char(string='Note Finance') diff --git a/indoteknik_custom/models/report_stock_forecasted.py b/indoteknik_custom/models/report_stock_forecasted.py index 92da13d5..d5e48fdc 100644 --- a/indoteknik_custom/models/report_stock_forecasted.py +++ b/indoteknik_custom/models/report_stock_forecasted.py @@ -10,28 +10,29 @@ class ReplenishmentReport(models.AbstractModel): for line in lines: document_out = line.get('document_out') - order_id = document_out.id if document_out else None - product_id = line.get('product', {}).get('id') - query = [('product_id', '=', product_id)] - if order_id: - result = self._calculate_result(line) - quantity = line.get('quantity', 0) - result_dict.setdefault(order_id, []).append((result, quantity)) + if document_out and "SO/" in document_out.name: + order_id = document_out.id + product_id = line.get('product', {}).get('id') + query = [('product_id', '=', product_id)] - for order_id, results in result_dict.items(): - sales_order = self.env['sale.order'].browse(order_id) + if order_id: + result = self._calculate_result(line) + quantity = line.get('quantity', 0) + result_dict.setdefault(order_id, []).append((result, quantity)) - for result, quantity in results: - self.env['sales.order.fullfillment'].create({ - 'sales_order_id': sales_order.id, - 'product_id': product_id, - 'reserved_from': result, - 'qty_fullfillment': quantity, - }) + for order_id, results in result_dict.items(): + sales_order = self.env['sale.order'].browse(order_id) + for result, quantity in results: + self.env['sales.order.fullfillment'].create({ + 'sales_order_id': sales_order.id, + 'product_id': product_id, + 'reserved_from': result, + 'qty_fullfillment': quantity, + }) - return lines + return lines def _calculate_result(self, line): if line['document_in']: diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 91792b59..ac81737d 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -2,6 +2,7 @@ from odoo import fields, models, api, _ from odoo.exceptions import UserError, ValidationError from datetime import datetime, timedelta import logging, random, string, requests, math, json, re +from collections import defaultdict _logger = logging.getLogger(__name__) @@ -9,7 +10,7 @@ _logger = logging.getLogger(__name__) class SaleOrder(models.Model): _inherit = "sale.order" - # fullfillment_line = fields.One2many('sales.order.fullfillment', 'sales_order_id', string='Fullfillment') + fullfillment_line = fields.One2many('sales.order.fullfillment', 'sales_order_id', string='Fullfillment') order_sales_match_line = fields.One2many('sales.order.purchase.match', 'sales_order_id', string='Purchase Match Lines', states={'cancel': [('readonly', True)], 'done': [('readonly', True)]}, copy=True) 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") @@ -90,22 +91,31 @@ class SaleOrder(models.Model): picking_iu_id = fields.Many2one('stock.picking', 'Picking IU') helper_by_id = fields.Many2one('res.users', 'Helper By') eta_date = fields.Datetime(string='ETA Date', copy=False, compute='_compute_eta_date') + flash_sale = fields.Boolean(string='Flash Sale', help='Data dari web') web_approval = fields.Selection([ ('company', 'Company'), ('cust_manager', 'Customer Manager'), ('cust_director', 'Customer Director'), ('cust_procurement', 'Customer Procurement') ], string='Web Approval', copy=False) - # compute_fullfillment = fields.Boolean(string='Compute Fullfillment', compute="_compute_fullfillment") + compute_fullfillment = fields.Boolean(string='Compute Fullfillment', compute="_compute_fullfillment") + note_ekspedisi = fields.Char(string="Note Ekspedisi") + + def open_form_multi_create_uang_muka(self): + action = self.env['ir.actions.act_window']._for_xml_id('indoteknik_custom.action_sale_order_multi_uangmuka') + action['context'] = { + 'so_ids': [x.id for x in self] + } + return action - # def _compute_fullfillment(self): - # for rec in self: - # rec.fullfillment_line.unlink() + def _compute_fullfillment(self): + for rec in self: + rec.fullfillment_line.unlink() - # for line in rec.order_line: - # line._compute_reserved_from() + for line in rec.order_line: + line._compute_reserved_from() - # rec.compute_fullfillment = True + rec.compute_fullfillment = True def _compute_eta_date(self): max_leadtime = 0 @@ -453,6 +463,8 @@ class SaleOrder(models.Model): 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 not order.client_order_ref and order.create_date > datetime(2024, 6, 27): + raise UserError("Customer Reference kosong, di isi dengan NO PO jika PO tidak ada mohon ditulis Tanpa PO") 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') @@ -466,11 +478,75 @@ class SaleOrder(models.Model): raise UserError("Bisa langsung Confirm") - def send_notif_to_salesperson(self): - for rec in self: - if not rec.partner_id.main_parent_id.use_so_approval: continue - template = self.env.ref('indoteknik_custom.mail_template_sale_order_notification_to_salesperson') - template.send_mail(rec.id, force_send=True) + def send_notif_to_salesperson(self, cancel=False): + + if not cancel: + grouping_so = self.search([ + ('partner_id.parent_id.id', '=', self.partner_id.parent_id.id), + ('partner_id.site_id.id', '=', self.partner_id.site_id.id), + ]) + else: + grouping_so = self.search([ + ('partner_id.parent_id.id', '=', self.partner_id.parent_id.id), + ('partner_id.site_id.id', '=', self.partner_id.site_id.id), + ('id', '!=', self.id), + ]) + # Kelompokkan data berdasarkan salesperson + salesperson_data = {} + for rec in grouping_so: + if rec.user_id.id not in salesperson_data: + salesperson_data[rec.user_id.id] = {'name': rec.user_id.name, 'orders': [], 'total_amount': 0, 'sum_total_amount': 0, 'business_partner': '', 'site': ''} # Menetapkan nilai awal untuk 'site' + if rec.picking_ids: + if not any(picking.state in ['assigned', 'confirmed', 'waiting'] for picking in rec.picking_ids): + continue + if all(picking.state == 'done' for picking in rec.picking_ids): + continue + if all(picking.state == 'cancel' for picking in rec.picking_ids): + continue + if not rec.partner_id.main_parent_id.use_so_approval: + continue + order_total_amount = rec.amount_total # Mengakses langsung rec.amount_total + salesperson_data[rec.user_id.id]['orders'].append({ + 'order_name': rec.name, + 'parent_name': rec.partner_id.name, + 'site_name': rec.partner_id.site_id.name, + 'total_amount': rec.amount_total, + }) + salesperson_data[rec.user_id.id]['sum_total_amount'] += order_total_amount + salesperson_data[rec.user_id.id]['business_partner'] = grouping_so[0].partner_id.main_parent_id.name + salesperson_data[rec.user_id.id]['site'] = grouping_so[0].partner_id.site_id.name # Menambahkan nilai hanya jika ada + + # Kirim email untuk setiap salesperson + for salesperson_id, data in salesperson_data.items(): + if data['orders']: + # Buat isi tabel untuk email + table_content = '' + for order_data in data['orders']: + table_content += f""" + <tr> + <td>{order_data['order_name']}</td> + <td>{order_data['parent_name']}</td> + <td>{order_data['site_name']}</td> + <td>{order_data['total_amount']}</td> + </tr> + """ + + # Dapatkan email salesperson + salesperson_email = self.env['res.users'].browse(salesperson_id).partner_id.email + + # Kirim email hanya jika ada data yang dikumpulkan + template = self.env.ref('indoteknik_custom.mail_template_sale_order_notification_to_salesperson') + email_body = template.body_html.replace('${table_content}', table_content) + email_body = email_body.replace('${salesperson_name}', data['name']) + email_body = email_body.replace('${sum_total_amount}', str(data['sum_total_amount'])) + email_body = email_body.replace('${site}', str(data['site'])) + email_body = email_body.replace('${business_partner}', str(data['business_partner'])) + # Kirim email + self.env['mail.mail'].create({ + 'subject': 'Notification: Sale Orders', + 'body_html': email_body, + 'email_to': salesperson_email, + }).send() def action_confirm(self): for order in self: @@ -483,6 +559,9 @@ class SaleOrder(models.Model): if FROM_WEBSITE and main_parent.use_so_approval and order.web_approval not in ['cust_procurement', 'cust_director']: raise UserError("This order not yet approved by customer procurement or director") + + if not order.client_order_ref and order.create_date > datetime(2024, 6, 27): + raise UserError("Customer Reference kosong, di isi dengan NO PO jika PO tidak ada mohon ditulis Tanpa PO") 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') @@ -505,6 +584,7 @@ class SaleOrder(models.Model): def action_cancel(self): # TODO stephan prevent cancel if have invoice, do, and po + main_parent = self.partner_id.get_main_parent() if self._name != 'sale.order': return super(SaleOrder, self).action_cancel() @@ -520,6 +600,8 @@ class SaleOrder(models.Model): self.approval_status = False self.due_id = False + if main_parent.use_so_approval: + self.send_notif_to_salesperson(cancel=True) return super(SaleOrder, self).action_cancel() def validate_partner_invoice_due(self): diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py index 509e3d1c..d362d573 100644 --- a/indoteknik_custom/models/sale_order_line.py +++ b/indoteknik_custom/models/sale_order_line.py @@ -110,9 +110,13 @@ 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 + 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), + ('product_id', '=', self.product_id.id)], + limit=1, order='count_trx_po desc, count_trx_po_vendor desc') + self.purchase_price = self._get_valid_purchase_price(purchase_price) else: - # purchase_price = self.env['purchase.pricelist'].search( - # [('vendor_id', '=', self.vendor_id.id), ('product_id', '=', self.product_id.id)], limit=1) purchase_price = self.env['purchase.pricelist'].search( [('vendor_id', '=', self.vendor_id.id), ('product_id', '=', self.product_id.id)], limit=1, order='count_trx_po desc, count_trx_po_vendor desc') @@ -136,11 +140,15 @@ class SaleOrderLine(models.Model): super(SaleOrderLine, self).product_id_change() for line in self: if line.product_id and line.product_id.type == 'product': + query = [('product_id', '=', line.product_id.id)] + if line.product_id.x_manufacture.override_vendor_id: + query = [('product_id', '=', line.product_id.id), + ('vendor_id', '=', line.product_id.x_manufacture.override_vendor_id.id)] purchase_price = self.env['purchase.pricelist'].search( - [('product_id', '=', self.product_id.id)], limit=1, order='count_trx_po desc, count_trx_po_vendor desc') + query, limit=1, order='count_trx_po desc, count_trx_po_vendor desc') line.vendor_id = purchase_price.vendor_id line.tax_id = line.order_id.sales_tax_id - line.purchase_price = self._get_valid_purchase_price(purchase_price) + line.purchase_price = line._get_valid_purchase_price(purchase_price) line_name = ('[' + line.product_id.default_code + ']' if line.product_id.default_code else '') + ' ' + (line.product_id.name if line.product_id.name else '') + ' ' + \ ('(' + line.product_id.product_template_attribute_value_ids.name + ')' if line.product_id.product_template_attribute_value_ids.name else '') + ' ' + \ diff --git a/indoteknik_custom/models/sale_order_multi_uangmuka_penjualan.py b/indoteknik_custom/models/sale_order_multi_uangmuka_penjualan.py new file mode 100644 index 00000000..f9120290 --- /dev/null +++ b/indoteknik_custom/models/sale_order_multi_uangmuka_penjualan.py @@ -0,0 +1,136 @@ +from odoo import fields, models, _, api +from odoo.exceptions import UserError +from datetime import datetime +from odoo.http import request + +import logging, math + +_logger = logging.getLogger(__name__) + + +class PurchaseOrderMultiUangmukaPenjualan(models.TransientModel): + _name = 'sale.order.multi_uangmuka_penjualan' + _description = 'digunakan untuk membuat Uang Muka Penjualan' + + pay_amt = fields.Float(string='Uang Muka', help='berapa nilai yang terbentuk untuk COA Uang Muka Pembelian') + account_id = fields.Many2one('account.account', string='Bank Intransit', default=389, help='pilih COA intransit bank') + ongkir_amt = fields.Float(string='Ongkir', help='masukan nilai yang akan menjadi Pendapatan Ongkos Kirim') + selisih_amt = fields.Float(string='Selisih', help='masukan nilai yang akan menjadi Selisih Pembayaran') + total_amt = fields.Float(string='Total', help='Total yang akan masuk di journal entries') + + @api.onchange('pay_amt', 'ongkir_amt', 'selisih_amt') + def _compute_total_amt(self): + for o in self: + o.total_amt = o.pay_amt + o.ongkir_amt + o.selisih_amt + + def create_uangmukapenjualanselected(self): + so_ids = self._context['so_ids'] + orders = self.env['sale.order'].browse(so_ids) + po_is_uangmuka = self.env['sale.order'].search([ + ('id', 'in', [x.id for x in orders]) + ]) + + if not self.account_id: + raise UserError('Bank Intransit harus diisi') + if not self.env.user.has_group('account.group_account_manager'): + raise UserError('Hanya Finance yang dapat membuat Uang Muka penjualan') + + current_time = datetime.now() + + is_have_ongkir = is_have_selisih = False + if self.ongkir_amt > 0: + is_have_ongkir = True + if not math.isclose(self.selisih_amt, 0): + is_have_selisih = True + + partner_name = orders[0].partner_id.name + if orders[0].partner_id.parent_id: + partner_name = orders[0].partner_id.parent_id.name + + order_names = ', '.join([data.name for data in orders]) # Menggabungkan nama order menjadi satu string + ref_label = f'UANG MUKA PENJUALAN {order_names} {partner_name}' + param_header = { + 'ref': ref_label, + 'date': current_time, + 'journal_id': 11, + # 'sale_id': order.id, + } + + account_move = self.env['account.move'].create([param_header]) + debit_entries = [] + pay_amt = 0 + + + for order in orders: + _logger.info('Success Create Uang Muka P %s' % account_move.name) + partner_id = order.partner_id.parent_id.id if order.partner_id.parent_id else order.partner_id.id + partner = order.partner_id.parent_id.name if order.partner_id.parent_id else order.partner_id.name + + param_credit = { + 'move_id': account_move.id, + 'account_id': 449, # uang muka penjualan + 'partner_id': partner_id, + 'currency_id': 12, + 'debit': 0, + 'credit': order.amount_total, + 'name': f'UANG MUKA PENJUALAN {order.name} {partner}', + } + param_ongkir_credit = { + 'move_id': account_move.id, + 'account_id': 550, # pendapatan ongkos kirim + 'partner_id': partner_id, + 'currency_id': 12, + 'debit': 0, + 'credit': self.ongkir_amt, + 'name': f'UANG MUKA PENJUALAN {order.name} {partner}', + } + param_selisih_credit = { + 'move_id': account_move.id, + 'account_id': 561, # selisih pembayaran + 'partner_id': partner_id, + 'currency_id': 12, + 'debit': 0, + 'credit': self.selisih_amt, + 'name': f'UANG MUKA PENJUALAN {order.name} {partner}', + } + + debit_entries.append(param_credit) + pay_amt += order.amount_total + + if is_have_ongkir: + debit_entries.append(param_ongkir_credit) + pay_amt += self.ongkir_amt + + if is_have_selisih: + debit_entries.append(param_selisih_credit) + pay_amt += self.selisih_amt + + param_debit = { + 'move_id': account_move.id, + 'account_id': self.account_id.id, # intransit + 'partner_id': partner_id, + 'currency_id': 12, + 'debit': pay_amt, + 'credit': 0, + 'name': ref_label, + } + + debit_entries.append(param_debit) + + # Create all account.move.line entries at once + self.env['account.move.line'].create(debit_entries) + + return { + 'name': _('Journal Entries'), + 'view_mode': 'form', + 'res_model': 'account.move', + 'target': 'current', + 'view_id': False, + 'type': 'ir.actions.act_window', + 'res_id': account_move.id + } + + # def save_multi_create_uang_muka(self): + + # account_move = self.create_uangmukapenjualanselected(purchase, is_create_uangmuka) +
\ No newline at end of file diff --git a/indoteknik_custom/models/solr/apache_solr.py b/indoteknik_custom/models/solr/apache_solr.py index 397db53b..6560c9b5 100644 --- a/indoteknik_custom/models/solr/apache_solr.py +++ b/indoteknik_custom/models/solr/apache_solr.py @@ -294,23 +294,26 @@ class ApacheSolr(models.Model): return False def _solr_sync_recommendation(self, limit=100): - solr_model = self.env['apache.solr'] variants = self.env['product.product'].search([('solr_flag', '=', 2)], limit=limit) - # documents = [] + documents = [] + catch = {} for variant in variants: - # document = solr_model.get_doc('recommendation', variant.id) - document = {} - document.update({ - 'id': variant.id, - 'display_name_s': variant.display_name, - 'name_s': variant.name, - 'default_code_s': variant.default_code or '', - 'product_rating_f': variant.product_tmpl_id.virtual_rating, - 'template_id_i': variant.product_tmpl_id.id, - 'active_s': variant.active, - }) - # self.solr().add(docs=[document], softCommit=True) - # documents.append(document) - variant.solr_flag = 1 - _recommendation_solr.add(document) - # _recommendation_solr.add(documents) + try: + document = { + 'id': variant.id or 0, + 'display_name_s': variant.display_name or '', + 'name_s': variant.name or '', + 'default_code_s': variant.default_code or '', + 'product_rating_f': variant.product_tmpl_id.virtual_rating or 0, + 'template_id_i': variant.product_tmpl_id.id or 0, + 'active_s': str(variant.active) or 'false', + 'type_s': variant.product_tmpl_id.type or '', + } + variant.write({'solr_flag': 1}) # Ensure the flag is updated correctly + documents.append(document) + catch = document + except Exception as e: + _logger.error("Failed to add document to Solr: %s", e) + _logger.error("Document data: %s", catch) + _recommendation_solr.add(documents) + return True |
