From ff97522607a4e9dc9d576c3ab10ff4329c34736d Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Thu, 19 Oct 2023 12:13:08 +0630 Subject: [PATCH 1/6] [IMP] stock_move_obc_csv --- .../report/report_account_move_obc_csv.py | 119 +++++++++++------- 1 file changed, 73 insertions(+), 46 deletions(-) diff --git a/account_move_obc_csv/report/report_account_move_obc_csv.py b/account_move_obc_csv/report/report_account_move_obc_csv.py index 07715a25..c3944948 100644 --- a/account_move_obc_csv/report/report_account_move_obc_csv.py +++ b/account_move_obc_csv/report/report_account_move_obc_csv.py @@ -2,7 +2,8 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import csv -from collections import defaultdict +from itertools import groupby +from operator import attrgetter from odoo import _, models from odoo.exceptions import UserError @@ -134,55 +135,81 @@ def _update_vals(self, vals, line, move_analytic_accounts, drcr): vals[fields["project"][drcr]] = project.code or "" return vals - def _get_report_vals_dict(self, record): - accounting_date = record.date.strftime("%Y/%m/%d") - move_analytic_accounts = record.line_ids.analytic_line_ids.mapped("account_id") - # Sort lines so that the tax line(s) will come at the end of a journal entry - move_lines = record.line_ids.filtered(lambda x: not x.tax_line_id).sorted( - lambda x: abs(x.balance), reverse=True - ) - move_lines += record.line_ids.filtered(lambda x: x.tax_line_id) - vals_dict = defaultdict(dict) - first_debit, first_credit = True, True - line_count = 1 - purchase_line = record.stock_move_id.purchase_line_id - for line in move_lines: - first_line = False - if (line.debit and first_debit) or (line.credit and first_credit): - first_line = True - line_num = 1 if first_line else line_count - vals = vals_dict[1] if first_line else {} - vals["GL0010001"] = accounting_date - vals["GL0010008"] = record.name - remarks = line.name - if purchase_line: - # This adjustment is so that the accounting staff can easily find - # the related journal items for receipts based on the same string - # set on those for vendor bill lines. - remarks = "%s: %s, %s" % ( - purchase_line.order_id.name, - purchase_line.name, - line.name, - ) - vals["GL0011001"] = remarks - if line.debit: - vals = self._update_vals(vals, line, move_analytic_accounts, "dr") - first_debit = False - if line.credit: - vals = self._update_vals(vals, line, move_analytic_accounts, "cr") - first_credit = False - vals_dict[line_num] = vals - line_count += 1 - return vals_dict + def _get_report_vals_dict(self, records): + vals_list = [] + total_debit, total_credit = 0, 0 + credit_account = "" + for record in records: + vals_dict = {} + accounting_date = record.date.strftime("%Y/%m/%d") + move_analytic_accounts = record.line_ids.analytic_line_ids.mapped( + "account_id" + ) + # Sort lines so that the tax line(s) will come at the end of a journal entry + move_lines = record.line_ids.filtered(lambda x: not x.tax_line_id).sorted( + lambda x: abs(x.balance), reverse=True + ) + move_lines += record.line_ids.filtered(lambda x: x.tax_line_id) + purchase_line = record.stock_move_id.purchase_line_id + for line in move_lines: + vals_dict["GL0010001"] = accounting_date + vals_dict["GL0010008"] = record.name + remarks = line.name + if purchase_line: + # This adjustment is so that the accounting staff can easily find + # the related journal items for receipts based on the same string + # set on those for vendor bill lines. + remarks = "%s: %s, %s" % ( + purchase_line.order_id.name, + purchase_line.name, + line.name, + ) + vals_dict["GL0011001"] = remarks + + if line.debit: + vals_dict = self._update_vals( + vals_dict, line, move_analytic_accounts, "dr" + ) + total_debit += vals_dict["GL0012101"] + vals_dict["GL0012101"] = 0 + if line.credit: + vals_dict = self._update_vals( + vals_dict, line, move_analytic_accounts, "cr" + ) + total_credit += vals_dict["GL0013101"] + vals_dict["GL0013101"] = 0 + # Assuming have same account for the JE that have same picking + credit_account = vals_dict["GL0013002"] + debit_account = vals_dict["GL0012002"] + vals_dict["GL0013002"] = "" + vals_dict["GL0012002"] = "" + vals_list.append(vals_dict) + + # After processing all records for the same picking, assign the sums to the first line + if vals_list: + vals_list[0]["GL0012002"] = debit_account + vals_list[0]["GL0013002"] = credit_account + vals_list[0]["GL0012101"] = total_debit + vals_list[0]["GL0013101"] = total_credit + + return vals_list def generate_csv_report(self, writer, data, records): self._check_records(records) + sorted_records = sorted(records, key=lambda r: r.stock_move_id.picking_id.id) + move_grouped_records = { + k: list(v) + for k, v in groupby( + sorted_records, key=attrgetter("stock_move_id.picking_id.id") + ) + } writer.writeheader() - for record in records: - vals_dict = self._get_report_vals_dict(record) - for _k, v in sorted(vals_dict.items()): - writer.writerow(v) - record.is_exported = True + for _i, grouped_by_picking in move_grouped_records.items(): + vals_list = self._get_report_vals_dict(grouped_by_picking) + for vals_dict in vals_list: + writer.writerow(vals_dict) + for record in grouped_by_picking: + record.is_exported = True def csv_report_options(self): res = super().csv_report_options() From d0531e2352fd27ebd3f15224fba45339d16d5191 Mon Sep 17 00:00:00 2001 From: Rinaldi Firdaus Date: Fri, 20 Oct 2023 10:41:36 +0000 Subject: [PATCH 2/6] adjustment --- .../report/report_account_move_obc_csv.py | 129 ++++++++---------- 1 file changed, 58 insertions(+), 71 deletions(-) diff --git a/account_move_obc_csv/report/report_account_move_obc_csv.py b/account_move_obc_csv/report/report_account_move_obc_csv.py index c3944948..a6f50165 100644 --- a/account_move_obc_csv/report/report_account_move_obc_csv.py +++ b/account_move_obc_csv/report/report_account_move_obc_csv.py @@ -2,8 +2,8 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import csv +from collections import defaultdict from itertools import groupby -from operator import attrgetter from odoo import _, models from odoo.exceptions import UserError @@ -15,6 +15,7 @@ class AccountMoveObcCsv(models.AbstractModel): def _get_fields(self): return [ + "GL0010000", # asterisk "GL0010001", # date "GL0012002", # debit account "GL0012101", # debit base amount @@ -135,80 +136,66 @@ def _update_vals(self, vals, line, move_analytic_accounts, drcr): vals[fields["project"][drcr]] = project.code or "" return vals - def _get_report_vals_dict(self, records): - vals_list = [] - total_debit, total_credit = 0, 0 - credit_account = "" - for record in records: - vals_dict = {} - accounting_date = record.date.strftime("%Y/%m/%d") - move_analytic_accounts = record.line_ids.analytic_line_ids.mapped( - "account_id" - ) - # Sort lines so that the tax line(s) will come at the end of a journal entry - move_lines = record.line_ids.filtered(lambda x: not x.tax_line_id).sorted( - lambda x: abs(x.balance), reverse=True - ) - move_lines += record.line_ids.filtered(lambda x: x.tax_line_id) - purchase_line = record.stock_move_id.purchase_line_id - for line in move_lines: - vals_dict["GL0010001"] = accounting_date - vals_dict["GL0010008"] = record.name - remarks = line.name - if purchase_line: - # This adjustment is so that the accounting staff can easily find - # the related journal items for receipts based on the same string - # set on those for vendor bill lines. - remarks = "%s: %s, %s" % ( - purchase_line.order_id.name, - purchase_line.name, - line.name, - ) - vals_dict["GL0011001"] = remarks - - if line.debit: - vals_dict = self._update_vals( - vals_dict, line, move_analytic_accounts, "dr" - ) - total_debit += vals_dict["GL0012101"] - vals_dict["GL0012101"] = 0 - if line.credit: - vals_dict = self._update_vals( - vals_dict, line, move_analytic_accounts, "cr" - ) - total_credit += vals_dict["GL0013101"] - vals_dict["GL0013101"] = 0 - # Assuming have same account for the JE that have same picking - credit_account = vals_dict["GL0013002"] - debit_account = vals_dict["GL0012002"] - vals_dict["GL0013002"] = "" - vals_dict["GL0012002"] = "" - vals_list.append(vals_dict) - - # After processing all records for the same picking, assign the sums to the first line - if vals_list: - vals_list[0]["GL0012002"] = debit_account - vals_list[0]["GL0013002"] = credit_account - vals_list[0]["GL0012101"] = total_debit - vals_list[0]["GL0013101"] = total_credit - - return vals_list + def _get_report_vals_dict(self, record): + accounting_date = record.date.strftime("%Y/%m/%d") + move_analytic_accounts = record.line_ids.analytic_line_ids.mapped("account_id") + # Sort lines so that the tax line(s) will come at the end of a journal entry + move_lines = record.line_ids.filtered(lambda x: not x.tax_line_id).sorted( + lambda x: abs(x.balance), reverse=True + ) + move_lines += record.line_ids.filtered(lambda x: x.tax_line_id) + vals_dict = defaultdict(dict) + first_debit, first_credit = True, True + line_count = 1 + purchase_line = record.stock_move_id.purchase_line_id + for line in move_lines: + first_line = False + if (line.debit and first_debit) or (line.credit and first_credit): + first_line = True + line_num = 1 if first_line else line_count + vals = vals_dict[1] if first_line else {} + vals["GL0010001"] = accounting_date + vals["GL0010008"] = record.name + remarks = line.name + if purchase_line: + # This adjustment is so that the accounting staff can easily find + # the related journal items for receipts based on the same string + # set on those for vendor bill lines. + remarks = "%s: %s, %s" % ( + purchase_line.order_id.name, + purchase_line.name, + line.name, + ) + vals["GL0011001"] = remarks + if line.debit: + vals = self._update_vals(vals, line, move_analytic_accounts, "dr") + first_debit = False + if line.credit: + vals = self._update_vals(vals, line, move_analytic_accounts, "cr") + first_credit = False + vals_dict[line_num] = vals + line_count += 1 + return vals_dict def generate_csv_report(self, writer, data, records): self._check_records(records) - sorted_records = sorted(records, key=lambda r: r.stock_move_id.picking_id.id) - move_grouped_records = { - k: list(v) - for k, v in groupby( - sorted_records, key=attrgetter("stock_move_id.picking_id.id") - ) - } + # Sort records by date and then by picking_id + sorted_records = sorted( + records, key=lambda r: (r.date, r.stock_move_id.picking_id.id) + ) writer.writeheader() - for _i, grouped_by_picking in move_grouped_records.items(): - vals_list = self._get_report_vals_dict(grouped_by_picking) - for vals_dict in vals_list: - writer.writerow(vals_dict) - for record in grouped_by_picking: + # Group records by picking_id + for _p, grouped_records in groupby( + sorted_records, key=lambda r: r.stock_move_id.picking_id.id + ): + first_record = True + for record in grouped_records: + vals_dict = self._get_report_vals_dict(record) + if first_record: + vals_dict[1]["GL0010000"] = "*" + first_record = False + for _k, v in sorted(vals_dict.items()): + writer.writerow(v) record.is_exported = True def csv_report_options(self): From edf9170e56f720eb1c41c599070c0d3927fc199b Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Wed, 25 Oct 2023 08:47:41 +0630 Subject: [PATCH 3/6] adjustments --- .../report/report_account_move_obc_csv.py | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/account_move_obc_csv/report/report_account_move_obc_csv.py b/account_move_obc_csv/report/report_account_move_obc_csv.py index a6f50165..bdb95063 100644 --- a/account_move_obc_csv/report/report_account_move_obc_csv.py +++ b/account_move_obc_csv/report/report_account_move_obc_csv.py @@ -3,7 +3,6 @@ import csv from collections import defaultdict -from itertools import groupby from odoo import _, models from odoo.exceptions import UserError @@ -120,6 +119,13 @@ def _update_vals(self, vals, line, move_analytic_accounts, drcr): != line.product_id.categ_id.property_stock_account_input_categ_id ): tax = line.tax_ids[:1] + partner_ref = line.partner_id.ref or "" + stock_move_id = line.move_id.stock_move_id + if ( + not line.partner_id + and stock_move_id.location_dest_id.is_subcontracting_location + ): + partner_ref = stock_move_id.group_id.partner_id.ref or "" fields = self._get_field_map() vals[fields["account"][drcr]] = account_code vals[fields["base_amount"][drcr]] = line.debit if drcr == "dr" else line.credit @@ -132,7 +138,7 @@ def _update_vals(self, vals, line, move_analytic_accounts, drcr): ) vals[fields["tax_rate"][drcr]] = tax.amount or 0 vals[fields["tax_auto_calc"][drcr]] = 0 # No tax calculation - vals[fields["partner"][drcr]] = line.partner_id.ref or "" + vals[fields["partner"][drcr]] = partner_ref vals[fields["project"][drcr]] = project.code or "" return vals @@ -184,19 +190,21 @@ def generate_csv_report(self, writer, data, records): records, key=lambda r: (r.date, r.stock_move_id.picking_id.id) ) writer.writeheader() - # Group records by picking_id - for _p, grouped_records in groupby( - sorted_records, key=lambda r: r.stock_move_id.picking_id.id - ): - first_record = True - for record in grouped_records: - vals_dict = self._get_report_vals_dict(record) - if first_record: - vals_dict[1]["GL0010000"] = "*" - first_record = False - for _k, v in sorted(vals_dict.items()): - writer.writerow(v) - record.is_exported = True + picking = self.env["stock.picking"] + for record in sorted_records: + vals_dict = self._get_report_vals_dict(record) + first_record = False + record_picking = record.stock_move_id.picking_id + if not record_picking: + first_record = True + elif picking != record_picking: + first_record = True + picking = record_picking + if first_record: + vals_dict[1]["GL0010000"] = "*" + for _k, v in sorted(vals_dict.items()): + writer.writerow(v) + record.is_exported = True def csv_report_options(self): res = super().csv_report_options() From 5f32a073e5903b62ecbebed62c8fd3d83ed0bb63 Mon Sep 17 00:00:00 2001 From: Yoshi Tashiro Date: Wed, 25 Oct 2023 07:52:49 +0000 Subject: [PATCH 4/6] adjust logic --- account_move_obc_csv/__manifest__.py | 3 ++- .../report/report_account_move_obc_csv.py | 25 ++++++++++++------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/account_move_obc_csv/__manifest__.py b/account_move_obc_csv/__manifest__.py index 97b39378..a59e55f8 100644 --- a/account_move_obc_csv/__manifest__.py +++ b/account_move_obc_csv/__manifest__.py @@ -2,13 +2,14 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { "name": "Account Move OBC CSV", - "version": "16.0.1.0.0", + "version": "16.0.1.0.1", "category": "Accounting", "author": "Quartile Limited", "website": "https://www.quartile.co", "license": "AGPL-3", "depends": [ "account", + "mrp_subcontracting", "report_csv", ], "data": [ diff --git a/account_move_obc_csv/report/report_account_move_obc_csv.py b/account_move_obc_csv/report/report_account_move_obc_csv.py index bdb95063..1ff66ae3 100644 --- a/account_move_obc_csv/report/report_account_move_obc_csv.py +++ b/account_move_obc_csv/report/report_account_move_obc_csv.py @@ -4,7 +4,7 @@ import csv from collections import defaultdict -from odoo import _, models +from odoo import _, api, models from odoo.exceptions import UserError @@ -76,6 +76,15 @@ def _check_records(self, records): % ("\n".join(exported_records.mapped("name"))) ) + @api.model + def _get_partner(self, line): + if line.partner_id: + return line.partner_id + # Try identifying the subcontracted partner from the production order. + stock_move = line.move_id.stock_move_id + production = stock_move.production_id or stock_move.raw_material_production_id + return production.subcontractor_id + def _update_vals(self, vals, line, move_analytic_accounts, drcr): account_code = line.account_id.code subaccount_code = "" @@ -119,13 +128,7 @@ def _update_vals(self, vals, line, move_analytic_accounts, drcr): != line.product_id.categ_id.property_stock_account_input_categ_id ): tax = line.tax_ids[:1] - partner_ref = line.partner_id.ref or "" - stock_move_id = line.move_id.stock_move_id - if ( - not line.partner_id - and stock_move_id.location_dest_id.is_subcontracting_location - ): - partner_ref = stock_move_id.group_id.partner_id.ref or "" + partner = self._get_partner(line) fields = self._get_field_map() vals[fields["account"][drcr]] = account_code vals[fields["base_amount"][drcr]] = line.debit if drcr == "dr" else line.credit @@ -138,7 +141,7 @@ def _update_vals(self, vals, line, move_analytic_accounts, drcr): ) vals[fields["tax_rate"][drcr]] = tax.amount or 0 vals[fields["tax_auto_calc"][drcr]] = 0 # No tax calculation - vals[fields["partner"][drcr]] = partner_ref + vals[fields["partner"][drcr]] = partner.ref or "" vals[fields["project"][drcr]] = project.code or "" return vals @@ -193,6 +196,10 @@ def generate_csv_report(self, writer, data, records): picking = self.env["stock.picking"] for record in sorted_records: vals_dict = self._get_report_vals_dict(record) + # Assign a demarkation symbol ('*') to the first journal entry of a picking + # to consolidate the entries into one record in the OBC system. This is to + # ease the operation of attaching the proof documents to the entries in the + # system. first_record = False record_picking = record.stock_move_id.picking_id if not record_picking: From 402537814bb9c88945731666f59dfe8858629437 Mon Sep 17 00:00:00 2001 From: Aungkokolin1997 Date: Thu, 26 Oct 2023 17:24:49 +0630 Subject: [PATCH 5/6] update for MO --- .../report/report_account_move_obc_csv.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/account_move_obc_csv/report/report_account_move_obc_csv.py b/account_move_obc_csv/report/report_account_move_obc_csv.py index 1ff66ae3..56f4e4e8 100644 --- a/account_move_obc_csv/report/report_account_move_obc_csv.py +++ b/account_move_obc_csv/report/report_account_move_obc_csv.py @@ -194,6 +194,7 @@ def generate_csv_report(self, writer, data, records): ) writer.writeheader() picking = self.env["stock.picking"] + mo = self.env["mrp.production"] for record in sorted_records: vals_dict = self._get_report_vals_dict(record) # Assign a demarkation symbol ('*') to the first journal entry of a picking @@ -202,11 +203,18 @@ def generate_csv_report(self, writer, data, records): # system. first_record = False record_picking = record.stock_move_id.picking_id - if not record_picking: + record_mo = ( + record.stock_move_id.production_id + or record.stock_move_id.raw_material_production_id + ) + if not record_picking and not record_mo: first_record = True - elif picking != record_picking: + elif not record_mo and picking != record_picking: first_record = True picking = record_picking + elif not record_picking and mo != record_mo: + first_record = True + mo = record_mo if first_record: vals_dict[1]["GL0010000"] = "*" for _k, v in sorted(vals_dict.items()): From 5be566f6c13017767f912df2ab4a098efa76646b Mon Sep 17 00:00:00 2001 From: Yoshi Tashiro Date: Thu, 26 Oct 2023 14:04:56 +0000 Subject: [PATCH 6/6] improve logic --- .../report/report_account_move_obc_csv.py | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/account_move_obc_csv/report/report_account_move_obc_csv.py b/account_move_obc_csv/report/report_account_move_obc_csv.py index 56f4e4e8..e09b7cfa 100644 --- a/account_move_obc_csv/report/report_account_move_obc_csv.py +++ b/account_move_obc_csv/report/report_account_move_obc_csv.py @@ -14,7 +14,7 @@ class AccountMoveObcCsv(models.AbstractModel): def _get_fields(self): return [ - "GL0010000", # asterisk + "GL0010000", # demarkator: indicates the start of a new OBC record "GL0010001", # date "GL0012002", # debit account "GL0012101", # debit base amount @@ -188,38 +188,42 @@ def _get_report_vals_dict(self, record): def generate_csv_report(self, writer, data, records): self._check_records(records) - # Sort records by date and then by picking_id + # Sort records by date, production id and picking id sorted_records = sorted( - records, key=lambda r: (r.date, r.stock_move_id.picking_id.id) + records, + key=lambda r: ( + r.date, + r.stock_move_id.production_id.id + or r.stock_move_id.raw_material_production_id.id, + r.stock_move_id.picking_id.id, + ), ) writer.writeheader() picking = self.env["stock.picking"] mo = self.env["mrp.production"] - for record in sorted_records: - vals_dict = self._get_report_vals_dict(record) + for rec in sorted_records: + vals_dict = self._get_report_vals_dict(rec) # Assign a demarkation symbol ('*') to the first journal entry of a picking - # to consolidate the entries into one record in the OBC system. This is to - # ease the operation of attaching the proof documents to the entries in the - # system. - first_record = False - record_picking = record.stock_move_id.picking_id - record_mo = ( - record.stock_move_id.production_id - or record.stock_move_id.raw_material_production_id - ) - if not record_picking and not record_mo: - first_record = True - elif not record_mo and picking != record_picking: - first_record = True - picking = record_picking - elif not record_picking and mo != record_mo: - first_record = True - mo = record_mo + # or a production order to consolidate the entries into one record in the + # OBC system. This is to ease the operation of attaching the proof documents + # to the entries in the system. + first_record = True + stock_move = rec.stock_move_id + rec_mo = stock_move.production_id or stock_move.raw_material_production_id + rec_picking = stock_move.picking_id + if rec_mo: + if rec_mo == mo: + first_record = False + mo = rec_mo + elif rec_picking: + if rec_picking == picking: + first_record = False + picking = rec_picking if first_record: vals_dict[1]["GL0010000"] = "*" for _k, v in sorted(vals_dict.items()): writer.writerow(v) - record.is_exported = True + rec.is_exported = True def csv_report_options(self): res = super().csv_report_options()