From f9d89c7ce6a877602d9a94b1b4496dd1a594db21 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 14 Apr 2022 18:34:47 +0530 Subject: [PATCH 1/9] fix: SO analysis rpt will fetch SO's without Delivery note as well (cherry picked from commit e28e6726f17954ba292a57e0c90d4b28a143f5e9) # Conflicts: # erpnext/selling/report/sales_order_analysis/sales_order_analysis.py --- .../report/sales_order_analysis/sales_order_analysis.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py index 465bb2d7452..b284ffc0e05 100644 --- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py +++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py @@ -77,7 +77,15 @@ def get_data(conditions, filters): `tabSales Order` so, `tabSales Order Item` soi LEFT JOIN `tabSales Invoice Item` sii +<<<<<<< HEAD ON sii.so_detail = soi.name and sii.docstatus = 1 +======= + ON sii.so_detail = soi.name and sii.docstatus = 1) + LEFT JOIN `tabDelivery Note Item` dni + on dni.so_detail = soi.name + LEFT JOIN `tabDelivery Note` dn + on dni.parent = dn.name and dn.docstatus = 1 +>>>>>>> e28e6726f1 (fix: SO analysis rpt will fetch SO's without Delivery note as well) WHERE soi.parent = so.name and so.status not in ('Stopped', 'Closed', 'On Hold') From 61ff1c22b366d167d56973ab12c50c3085a98339 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 16 Apr 2022 10:25:44 +0530 Subject: [PATCH 2/9] test: Sales order analysis report (cherry picked from commit 13487e240880306f2e5341eaba4dc350e1e97783) --- .../test_sales_order_analysis.py | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 erpnext/selling/report/sales_order_analysis/test_sales_order_analysis.py diff --git a/erpnext/selling/report/sales_order_analysis/test_sales_order_analysis.py b/erpnext/selling/report/sales_order_analysis/test_sales_order_analysis.py new file mode 100644 index 00000000000..25cbb734499 --- /dev/null +++ b/erpnext/selling/report/sales_order_analysis/test_sales_order_analysis.py @@ -0,0 +1,166 @@ +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import add_days + +from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note, make_sales_invoice +from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order +from erpnext.selling.report.sales_order_analysis.sales_order_analysis import execute +from erpnext.stock.doctype.item.test_item import create_item + +test_dependencies = ["Sales Order", "Item", "Sales Invoice", "Delivery Note"] + + +class TestSalesOrderAnalysis(FrappeTestCase): + def create_sales_order(self, transaction_date): + item = create_item(item_code="_Test Excavator", is_stock_item=0) + so = make_sales_order( + transaction_date=transaction_date, + item=item.item_code, + qty=10, + rate=100000, + do_not_save=True, + ) + so.po_no = "" + so.taxes_and_charges = "" + so.taxes = "" + so.items[0].delivery_date = add_days(transaction_date, 15) + so.save() + so.submit() + return item, so + + def create_sales_invoice(self, so): + sinv = make_sales_invoice(so.name) + sinv.posting_date = so.transaction_date + sinv.taxes_and_charges = "" + sinv.taxes = "" + sinv.insert() + sinv.submit() + return sinv + + def create_delivery_note(self, so): + dn = make_delivery_note(so.name) + dn.set_posting_time = True + dn.posting_date = add_days(so.transaction_date, 1) + dn.save() + dn.submit() + return dn + + def test_01_so_to_deliver_and_bill(self): + transaction_date = "2021-06-01" + item, so = self.create_sales_order(transaction_date) + columns, data, message, chart = execute( + { + "company": "_Test Company", + "from_date": "2021-06-01", + "to_date": "2021-06-30", + "status": ["To Deliver and Bill"], + } + ) + expected_value = { + "status": "To Deliver and Bill", + "sales_order": so.name, + "delay_days": frappe.utils.date_diff(frappe.utils.datetime.date.today(), so.delivery_date), + "qty": 10, + "delivered_qty": 0, + "pending_qty": 10, + "qty_to_bill": 10, + "time_taken_to_deliver": 0, + } + self.assertEqual(len(data), 1) + for key, val in expected_value.items(): + with self.subTest(key=key, val=val): + self.assertEqual(data[0][key], val) + + def test_02_so_to_deliver(self): + transaction_date = "2021-06-01" + item, so = self.create_sales_order(transaction_date) + self.create_sales_invoice(so) + columns, data, message, chart = execute( + { + "company": "_Test Company", + "from_date": "2021-06-01", + "to_date": "2021-06-30", + "status": ["To Deliver"], + } + ) + expected_value = { + "status": "To Deliver", + "sales_order": so.name, + "delay_days": frappe.utils.date_diff(frappe.utils.datetime.date.today(), so.delivery_date), + "qty": 10, + "delivered_qty": 0, + "pending_qty": 10, + "qty_to_bill": 0, + "time_taken_to_deliver": 0, + } + self.assertEqual(len(data), 1) + for key, val in expected_value.items(): + with self.subTest(key=key, val=val): + self.assertEqual(data[0][key], val) + + def test_03_so_to_bill(self): + transaction_date = "2021-06-01" + item, so = self.create_sales_order(transaction_date) + self.create_delivery_note(so) + columns, data, message, chart = execute( + { + "company": "_Test Company", + "from_date": "2021-06-01", + "to_date": "2021-06-30", + "status": ["To Bill"], + } + ) + expected_value = { + "status": "To Bill", + "sales_order": so.name, + "delay_days": frappe.utils.date_diff(frappe.utils.datetime.date.today(), so.delivery_date), + "qty": 10, + "delivered_qty": 10, + "pending_qty": 0, + "qty_to_bill": 10, + "time_taken_to_deliver": 86400, + } + self.assertEqual(len(data), 1) + for key, val in expected_value.items(): + with self.subTest(key=key, val=val): + self.assertEqual(data[0][key], val) + + def test_04_so_completed(self): + transaction_date = "2021-06-01" + item, so = self.create_sales_order(transaction_date) + self.create_sales_invoice(so) + self.create_delivery_note(so) + columns, data, message, chart = execute( + { + "company": "_Test Company", + "from_date": "2021-06-01", + "to_date": "2021-06-30", + "status": ["Completed"], + } + ) + expected_value = { + "status": "Completed", + "sales_order": so.name, + "delay_days": frappe.utils.date_diff(frappe.utils.datetime.date.today(), so.delivery_date), + "qty": 10, + "delivered_qty": 10, + "pending_qty": 0, + "qty_to_bill": 0, + "billed_qty": 10, + "time_taken_to_deliver": 86400, + } + self.assertEqual(len(data), 1) + for key, val in expected_value.items(): + with self.subTest(key=key, val=val): + self.assertEqual(data[0][key], val) + + def test_05_all_so_status(self): + columns, data, message, chart = execute( + { + "company": "_Test Company", + "from_date": "2021-06-01", + "to_date": "2021-06-30", + } + ) + # SO's from first 4 test cases should be in output + self.assertEqual(len(data), 4) From 38002e3fe265c91664c31a9dc4ab8fac9cb01631 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 19 Apr 2022 12:39:28 +0530 Subject: [PATCH 3/9] fix(patch): check if column is present while fixing reverse linking (backport #30737) (#30739) Co-authored-by: Rucha Mahabal --- ...itional_salary_encashment_and_incentive.py | 98 ++++++++++--------- 1 file changed, 51 insertions(+), 47 deletions(-) diff --git a/erpnext/patches/v13_0/patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive.py b/erpnext/patches/v13_0/patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive.py index edd0a9706b9..45acf49205b 100644 --- a/erpnext/patches/v13_0/patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive.py +++ b/erpnext/patches/v13_0/patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive.py @@ -10,54 +10,58 @@ def execute(): frappe.reload_doc("hr", "doctype", "Leave Encashment") - additional_salaries = frappe.get_all( - "Additional Salary", - fields=["name", "salary_slip", "type", "salary_component"], - filters={"salary_slip": ["!=", ""]}, - group_by="salary_slip", - ) - leave_encashments = frappe.get_all( - "Leave Encashment", - fields=["name", "additional_salary"], - filters={"additional_salary": ["!=", ""]}, - ) - employee_incentives = frappe.get_all( - "Employee Incentive", - fields=["name", "additional_salary"], - filters={"additional_salary": ["!=", ""]}, - ) - - for incentive in employee_incentives: - frappe.db.sql( - """ UPDATE `tabAdditional Salary` - SET ref_doctype = 'Employee Incentive', ref_docname = %s - WHERE name = %s - """, - (incentive["name"], incentive["additional_salary"]), + if frappe.db.has_column("Leave Encashment", "additional_salary"): + leave_encashments = frappe.get_all( + "Leave Encashment", + fields=["name", "additional_salary"], + filters={"additional_salary": ["!=", ""]}, ) - - for leave_encashment in leave_encashments: - frappe.db.sql( - """ UPDATE `tabAdditional Salary` - SET ref_doctype = 'Leave Encashment', ref_docname = %s - WHERE name = %s - """, - (leave_encashment["name"], leave_encashment["additional_salary"]), - ) - - salary_slips = [sal["salary_slip"] for sal in additional_salaries] - - for salary in additional_salaries: - comp_type = "earnings" if salary["type"] == "Earning" else "deductions" - if salary["salary_slip"] and salary_slips.count(salary["salary_slip"]) == 1: + for leave_encashment in leave_encashments: frappe.db.sql( - """ - UPDATE `tabSalary Detail` - SET additional_salary = %s - WHERE parenttype = 'Salary Slip' - and parentfield = %s - and parent = %s - and salary_component = %s + """ UPDATE `tabAdditional Salary` + SET ref_doctype = 'Leave Encashment', ref_docname = %s + WHERE name = %s """, - (salary["name"], comp_type, salary["salary_slip"], salary["salary_component"]), + (leave_encashment["name"], leave_encashment["additional_salary"]), ) + + if frappe.db.has_column("Employee Incentive", "additional_salary"): + employee_incentives = frappe.get_all( + "Employee Incentive", + fields=["name", "additional_salary"], + filters={"additional_salary": ["!=", ""]}, + ) + + for incentive in employee_incentives: + frappe.db.sql( + """ UPDATE `tabAdditional Salary` + SET ref_doctype = 'Employee Incentive', ref_docname = %s + WHERE name = %s + """, + (incentive["name"], incentive["additional_salary"]), + ) + + if frappe.db.has_column("Additional Salary", "salary_slip"): + additional_salaries = frappe.get_all( + "Additional Salary", + fields=["name", "salary_slip", "type", "salary_component"], + filters={"salary_slip": ["!=", ""]}, + group_by="salary_slip", + ) + + salary_slips = [sal["salary_slip"] for sal in additional_salaries] + + for salary in additional_salaries: + comp_type = "earnings" if salary["type"] == "Earning" else "deductions" + if salary["salary_slip"] and salary_slips.count(salary["salary_slip"]) == 1: + frappe.db.sql( + """ + UPDATE `tabSalary Detail` + SET additional_salary = %s + WHERE parenttype = 'Salary Slip' + and parentfield = %s + and parent = %s + and salary_component = %s + """, + (salary["name"], comp_type, salary["salary_slip"], salary["salary_component"]), + ) From baab3797ca1b151862d4deb388a85a33f3bbcbd3 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 19 Apr 2022 15:28:56 +0530 Subject: [PATCH 4/9] fix: Update token to allow updates on protected branch (cherry picked from commit 6f332f3669d81dd08828077ba91334450531dcfa) --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 532485f21f9..32ea02b1d71 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,5 +21,5 @@ jobs: npm install @semantic-release/git @semantic-release/exec --no-save - name: Create Release env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} run: npx semantic-release \ No newline at end of file From 6291b28c37f02380001ab68b3a660b2804383ab2 Mon Sep 17 00:00:00 2001 From: "FinByz Tech Pvt. Ltd" Date: Tue, 19 Apr 2022 16:26:56 +0530 Subject: [PATCH 5/9] fix(india): transporter name is null while generating e-way bill (#30736) --- erpnext/regional/india/e_invoice/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py index 2b5d17710eb..0036a2bf721 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -1073,7 +1073,7 @@ class GSPConnector: "Distance": cint(eway_bill_details.distance), "TransMode": eway_bill_details.mode_of_transport, "TransId": eway_bill_details.gstin, - "TransName": eway_bill_details.transporter, + "TransName": eway_bill_details.name, "TrnDocDt": eway_bill_details.document_date, "TrnDocNo": eway_bill_details.document_name, "VehNo": eway_bill_details.vehicle_no, From 77e8c542dd7ae4c4e6cdf8bd5b8b0a1fbb3ea51d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 19 Apr 2022 17:33:15 +0530 Subject: [PATCH 6/9] chore: Update creds to allow updates on protected branch (#30749) (#30750) (cherry picked from commit e4265ce814bb417ff5acd79e1bedae1b6a6d617b) Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> --- .github/workflows/release.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 32ea02b1d71..5a46002820c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,6 +12,7 @@ jobs: uses: actions/checkout@v2 with: fetch-depth: 0 + persist-credentials: false - name: Setup Node.js v14 uses: actions/setup-node@v2 with: @@ -21,5 +22,10 @@ jobs: npm install @semantic-release/git @semantic-release/exec --no-save - name: Create Release env: + GH_TOKEN: ${{ secrets.RELEASE_TOKEN }} GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} + GIT_AUTHOR_NAME: "Frappe PR Bot" + GIT_AUTHOR_EMAIL: "developers@frappe.io" + GIT_COMMITTER_NAME: "Frappe PR Bot" + GIT_COMMITTER_EMAIL: "developers@frappe.io" run: npx semantic-release \ No newline at end of file From 0b4e3f1467662b4ad86ac9f101cd95620aaa1c26 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Wed, 20 Apr 2022 08:59:45 +0200 Subject: [PATCH 7/9] fix: monthly attendance sheet (#30748) --- .../monthly_attendance_sheet.py | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py index e3cb36e0daf..c6f5bf05891 100644 --- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py +++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py @@ -57,11 +57,10 @@ def execute(filters=None): data = [] - leave_list = None + leave_types = None if filters.summarized_view: - leave_types = frappe.db.sql("""select name from `tabLeave Type`""", as_list=True) - leave_list = [d[0] + ":Float:120" for d in leave_types] - columns.extend(leave_list) + leave_types = frappe.get_all("Leave Type", pluck="name") + columns.extend([leave_type + ":Float:120" for leave_type in leave_types]) columns.extend([_("Total Late Entries") + ":Float:120", _("Total Early Exits") + ":Float:120"]) if filters.group_by: @@ -81,13 +80,19 @@ def execute(filters=None): holiday_map, conditions, default_holiday_list, - leave_list=leave_list, + leave_types=leave_types, ) emp_att_map.update(emp_att_data) data += record else: record, emp_att_map = add_data( - emp_map, att_map, filters, holiday_map, conditions, default_holiday_list, leave_list=leave_list + emp_map, + att_map, + filters, + holiday_map, + conditions, + default_holiday_list, + leave_types=leave_types, ) data += record @@ -104,12 +109,10 @@ def get_chart_data(emp_att_map, days): {"name": "Leave", "values": []}, ] for idx, day in enumerate(days, start=0): - p = day.replace("::65", "") labels.append(day.replace("::65", "")) total_absent_on_day = 0 total_leave_on_day = 0 total_present_on_day = 0 - total_holiday = 0 for emp in emp_att_map.keys(): if emp_att_map[emp][idx]: if emp_att_map[emp][idx] == "A": @@ -134,9 +137,8 @@ def get_chart_data(emp_att_map, days): def add_data( - employee_map, att_map, filters, holiday_map, conditions, default_holiday_list, leave_list=None + employee_map, att_map, filters, holiday_map, conditions, default_holiday_list, leave_types=None ): - record = [] emp_att_map = {} for emp in employee_map: @@ -222,7 +224,7 @@ def add_data( else: leaves[d.leave_type] = d.count - for d in leave_list: + for d in leave_types: if d in leaves: row.append(leaves[d]) else: From b656ffa45e2494b0a4e64ad8284a7da85c386899 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 20 Apr 2022 15:11:11 +0530 Subject: [PATCH 8/9] fix: Must not be able to start Job Card if it is related to Work Order that is not started yet (#29072) (#30755) * fix: Cannot start Job strat if related to Work Order not started yet * fix: Cannot start Job strat if related to Work Order not started yet * test * test * fix siders * PR review * chore: Code cleanup - Better short circuit for if condition (make it such that both conditions dont always have to be computed) - Remove `r.message` extraction by avoiding `then()` * chore: Remove unnecessary json change Co-authored-by: marination (cherry picked from commit 143786aaa077d2fb29d54836dcb8424ca247a8e8) Co-authored-by: HENRY Florian --- erpnext/manufacturing/doctype/job_card/job_card.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.js b/erpnext/manufacturing/doctype/job_card/job_card.js index c4541fa68e6..20bbbeaa8ea 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.js +++ b/erpnext/manufacturing/doctype/job_card/job_card.js @@ -73,7 +73,18 @@ frappe.ui.form.on('Job Card', { if (frm.doc.docstatus == 0 && !frm.is_new() && (frm.doc.for_quantity > frm.doc.total_completed_qty || !frm.doc.for_quantity) && (frm.doc.items || !frm.doc.items.length || frm.doc.for_quantity == frm.doc.transferred_qty)) { - frm.trigger("prepare_timer_buttons"); + + // if Job Card is link to Work Order, the job card must not be able to start if Work Order not "Started" + // and if stock mvt for WIP is required + if (frm.doc.work_order) { + frappe.db.get_value('Work Order', frm.doc.work_order, ['skip_transfer', 'status'], (result) => { + if (result.skip_transfer === 1 || result.status == 'In Process') { + frm.trigger("prepare_timer_buttons"); + } + }); + } else { + frm.trigger("prepare_timer_buttons"); + } } frm.trigger("setup_quality_inspection"); From 269e1923c9edf144224c3b81584ec626df359d8c Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Wed, 20 Apr 2022 19:44:09 +0530 Subject: [PATCH 9/9] fix: Use right precision for asset value after full schedule (#30745) --- erpnext/assets/doctype/asset/asset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 21d90b5e48a..c255348a2bf 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -628,7 +628,7 @@ class Asset(AccountsController): asset_value_after_full_schedule = flt( flt(self.gross_purchase_amount) - flt(accumulated_depreciation_after_full_schedule), - self.precision("gross_purchase_amount"), + row.precision("expected_value_after_useful_life"), ) if (