mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-31 10:49:09 +00:00
Merge branch 'version-13-hotfix' into patch-4
This commit is contained in:
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@@ -12,6 +12,7 @@ jobs:
|
|||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
persist-credentials: false
|
||||||
- name: Setup Node.js v14
|
- name: Setup Node.js v14
|
||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
@@ -21,5 +22,10 @@ jobs:
|
|||||||
npm install @semantic-release/git @semantic-release/exec --no-save
|
npm install @semantic-release/git @semantic-release/exec --no-save
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
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
|
run: npx semantic-release
|
||||||
@@ -628,7 +628,7 @@ class Asset(AccountsController):
|
|||||||
|
|
||||||
asset_value_after_full_schedule = flt(
|
asset_value_after_full_schedule = flt(
|
||||||
flt(self.gross_purchase_amount) - flt(accumulated_depreciation_after_full_schedule),
|
flt(self.gross_purchase_amount) - flt(accumulated_depreciation_after_full_schedule),
|
||||||
self.precision("gross_purchase_amount"),
|
row.precision("expected_value_after_useful_life"),
|
||||||
)
|
)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -57,11 +57,10 @@ def execute(filters=None):
|
|||||||
|
|
||||||
data = []
|
data = []
|
||||||
|
|
||||||
leave_list = None
|
leave_types = None
|
||||||
if filters.summarized_view:
|
if filters.summarized_view:
|
||||||
leave_types = frappe.db.sql("""select name from `tabLeave Type`""", as_list=True)
|
leave_types = frappe.get_all("Leave Type", pluck="name")
|
||||||
leave_list = [d[0] + ":Float:120" for d in leave_types]
|
columns.extend([leave_type + ":Float:120" for leave_type in leave_types])
|
||||||
columns.extend(leave_list)
|
|
||||||
columns.extend([_("Total Late Entries") + ":Float:120", _("Total Early Exits") + ":Float:120"])
|
columns.extend([_("Total Late Entries") + ":Float:120", _("Total Early Exits") + ":Float:120"])
|
||||||
|
|
||||||
if filters.group_by:
|
if filters.group_by:
|
||||||
@@ -81,13 +80,19 @@ def execute(filters=None):
|
|||||||
holiday_map,
|
holiday_map,
|
||||||
conditions,
|
conditions,
|
||||||
default_holiday_list,
|
default_holiday_list,
|
||||||
leave_list=leave_list,
|
leave_types=leave_types,
|
||||||
)
|
)
|
||||||
emp_att_map.update(emp_att_data)
|
emp_att_map.update(emp_att_data)
|
||||||
data += record
|
data += record
|
||||||
else:
|
else:
|
||||||
record, emp_att_map = add_data(
|
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
|
data += record
|
||||||
|
|
||||||
@@ -104,12 +109,10 @@ def get_chart_data(emp_att_map, days):
|
|||||||
{"name": "Leave", "values": []},
|
{"name": "Leave", "values": []},
|
||||||
]
|
]
|
||||||
for idx, day in enumerate(days, start=0):
|
for idx, day in enumerate(days, start=0):
|
||||||
p = day.replace("::65", "")
|
|
||||||
labels.append(day.replace("::65", ""))
|
labels.append(day.replace("::65", ""))
|
||||||
total_absent_on_day = 0
|
total_absent_on_day = 0
|
||||||
total_leave_on_day = 0
|
total_leave_on_day = 0
|
||||||
total_present_on_day = 0
|
total_present_on_day = 0
|
||||||
total_holiday = 0
|
|
||||||
for emp in emp_att_map.keys():
|
for emp in emp_att_map.keys():
|
||||||
if emp_att_map[emp][idx]:
|
if emp_att_map[emp][idx]:
|
||||||
if emp_att_map[emp][idx] == "A":
|
if emp_att_map[emp][idx] == "A":
|
||||||
@@ -134,9 +137,8 @@ def get_chart_data(emp_att_map, days):
|
|||||||
|
|
||||||
|
|
||||||
def add_data(
|
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 = []
|
record = []
|
||||||
emp_att_map = {}
|
emp_att_map = {}
|
||||||
for emp in employee_map:
|
for emp in employee_map:
|
||||||
@@ -222,7 +224,7 @@ def add_data(
|
|||||||
else:
|
else:
|
||||||
leaves[d.leave_type] = d.count
|
leaves[d.leave_type] = d.count
|
||||||
|
|
||||||
for d in leave_list:
|
for d in leave_types:
|
||||||
if d in leaves:
|
if d in leaves:
|
||||||
row.append(leaves[d])
|
row.append(leaves[d])
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -73,7 +73,18 @@ frappe.ui.form.on('Job Card', {
|
|||||||
if (frm.doc.docstatus == 0 && !frm.is_new() &&
|
if (frm.doc.docstatus == 0 && !frm.is_new() &&
|
||||||
(frm.doc.for_quantity > frm.doc.total_completed_qty || !frm.doc.for_quantity)
|
(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.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");
|
frm.trigger("setup_quality_inspection");
|
||||||
|
|
||||||
|
|||||||
@@ -10,54 +10,58 @@ def execute():
|
|||||||
|
|
||||||
frappe.reload_doc("hr", "doctype", "Leave Encashment")
|
frappe.reload_doc("hr", "doctype", "Leave Encashment")
|
||||||
|
|
||||||
additional_salaries = frappe.get_all(
|
if frappe.db.has_column("Leave Encashment", "additional_salary"):
|
||||||
"Additional Salary",
|
leave_encashments = frappe.get_all(
|
||||||
fields=["name", "salary_slip", "type", "salary_component"],
|
"Leave Encashment",
|
||||||
filters={"salary_slip": ["!=", ""]},
|
fields=["name", "additional_salary"],
|
||||||
group_by="salary_slip",
|
filters={"additional_salary": ["!=", ""]},
|
||||||
)
|
|
||||||
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"]),
|
|
||||||
)
|
)
|
||||||
|
for leave_encashment in leave_encashments:
|
||||||
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:
|
|
||||||
frappe.db.sql(
|
frappe.db.sql(
|
||||||
"""
|
""" UPDATE `tabAdditional Salary`
|
||||||
UPDATE `tabSalary Detail`
|
SET ref_doctype = 'Leave Encashment', ref_docname = %s
|
||||||
SET additional_salary = %s
|
WHERE name = %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"]),
|
(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"]),
|
||||||
|
)
|
||||||
|
|||||||
@@ -1073,7 +1073,7 @@ class GSPConnector:
|
|||||||
"Distance": cint(eway_bill_details.distance),
|
"Distance": cint(eway_bill_details.distance),
|
||||||
"TransMode": eway_bill_details.mode_of_transport,
|
"TransMode": eway_bill_details.mode_of_transport,
|
||||||
"TransId": eway_bill_details.gstin,
|
"TransId": eway_bill_details.gstin,
|
||||||
"TransName": eway_bill_details.transporter,
|
"TransName": eway_bill_details.name,
|
||||||
"TrnDocDt": eway_bill_details.document_date,
|
"TrnDocDt": eway_bill_details.document_date,
|
||||||
"TrnDocNo": eway_bill_details.document_name,
|
"TrnDocNo": eway_bill_details.document_name,
|
||||||
"VehNo": eway_bill_details.vehicle_no,
|
"VehNo": eway_bill_details.vehicle_no,
|
||||||
|
|||||||
@@ -77,7 +77,15 @@ def get_data(conditions, filters):
|
|||||||
`tabSales Order` so,
|
`tabSales Order` so,
|
||||||
`tabSales Order Item` soi
|
`tabSales Order Item` soi
|
||||||
LEFT JOIN `tabSales Invoice Item` sii
|
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
|
||||||
|
=======
|
||||||
|
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
|
WHERE
|
||||||
soi.parent = so.name
|
soi.parent = so.name
|
||||||
and so.status not in ('Stopped', 'Closed', 'On Hold')
|
and so.status not in ('Stopped', 'Closed', 'On Hold')
|
||||||
|
|||||||
@@ -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)
|
||||||
Reference in New Issue
Block a user