diff --git a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
index 9d2deea523b..449ebdcd924 100644
--- a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
+++ b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
@@ -22,8 +22,7 @@ def get_columns():
{
"label": _("Payment Document Type"),
"fieldname": "payment_document_type",
- "fieldtype": "Link",
- "options": "Doctype",
+ "fieldtype": "Data",
"width": 130,
},
{
@@ -33,15 +32,15 @@ def get_columns():
"options": "payment_document_type",
"width": 140,
},
- {"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 100},
+ {"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 120},
{"label": _("Cheque/Reference No"), "fieldname": "cheque_no", "width": 120},
- {"label": _("Clearance Date"), "fieldname": "clearance_date", "fieldtype": "Date", "width": 100},
+ {"label": _("Clearance Date"), "fieldname": "clearance_date", "fieldtype": "Date", "width": 120},
{
"label": _("Against Account"),
"fieldname": "against",
"fieldtype": "Link",
"options": "Account",
- "width": 170,
+ "width": 200,
},
{"label": _("Amount"), "fieldname": "amount", "fieldtype": "Currency", "width": 120},
]
diff --git a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py
index 3beaa2bfe74..f5b46bde586 100644
--- a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py
+++ b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py
@@ -27,6 +27,7 @@ class PartyLedgerSummaryReport(object):
)
self.get_gl_entries()
+ self.get_additional_columns()
self.get_return_invoices()
self.get_party_adjustment_amounts()
@@ -34,6 +35,42 @@ class PartyLedgerSummaryReport(object):
data = self.get_data()
return columns, data
+ def get_additional_columns(self):
+ """
+ Additional Columns for 'User Permission' based access control
+ """
+ from frappe import qb
+
+ if self.filters.party_type == "Customer":
+ self.territories = frappe._dict({})
+ self.customer_group = frappe._dict({})
+
+ customer = qb.DocType("Customer")
+ result = (
+ frappe.qb.from_(customer)
+ .select(
+ customer.name, customer.territory, customer.customer_group, customer.default_sales_partner
+ )
+ .where((customer.disabled == 0))
+ .run(as_dict=True)
+ )
+
+ for x in result:
+ self.territories[x.name] = x.territory
+ self.customer_group[x.name] = x.customer_group
+ else:
+ self.supplier_group = frappe._dict({})
+ supplier = qb.DocType("Supplier")
+ result = (
+ frappe.qb.from_(supplier)
+ .select(supplier.name, supplier.supplier_group)
+ .where((supplier.disabled == 0))
+ .run(as_dict=True)
+ )
+
+ for x in result:
+ self.supplier_group[x.name] = x.supplier_group
+
def get_columns(self):
columns = [
{
@@ -117,6 +154,35 @@ class PartyLedgerSummaryReport(object):
},
]
+ # Hidden columns for handling 'User Permissions'
+ if self.filters.party_type == "Customer":
+ columns += [
+ {
+ "label": _("Territory"),
+ "fieldname": "territory",
+ "fieldtype": "Link",
+ "options": "Territory",
+ "hidden": 1,
+ },
+ {
+ "label": _("Customer Group"),
+ "fieldname": "customer_group",
+ "fieldtype": "Link",
+ "options": "Customer Group",
+ "hidden": 1,
+ },
+ ]
+ else:
+ columns += [
+ {
+ "label": _("Supplier Group"),
+ "fieldname": "supplier_group",
+ "fieldtype": "Link",
+ "options": "Supplier Group",
+ "hidden": 1,
+ }
+ ]
+
return columns
def get_data(self):
@@ -144,6 +210,12 @@ class PartyLedgerSummaryReport(object):
),
)
+ if self.filters.party_type == "Customer":
+ self.party_data[gle.party].update({"territory": self.territories.get(gle.party)})
+ self.party_data[gle.party].update({"customer_group": self.customer_group.get(gle.party)})
+ else:
+ self.party_data[gle.party].update({"supplier_group": self.supplier_group.get(gle.party)})
+
amount = gle.get(invoice_dr_or_cr) - gle.get(reverse_dr_or_cr)
self.party_data[gle.party].closing_balance += amount
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py
index b71b31a5ec2..6bd3ee5aa6b 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/gross_profit.py
@@ -551,6 +551,7 @@ class GrossProfitGenerator(object):
return abs(previous_stock_value - flt(sle.stock_value)) * flt(row.qty) / abs(flt(sle.qty))
else:
return flt(row.qty) * self.get_average_buying_rate(row, item_code)
+ return 0.0
def get_buying_amount(self, row, item_code):
# IMP NOTE
diff --git a/erpnext/accounts/report/gross_profit/test_gross_profit.py b/erpnext/accounts/report/gross_profit/test_gross_profit.py
index 1279dec25af..d9febb74fd4 100644
--- a/erpnext/accounts/report/gross_profit/test_gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/test_gross_profit.py
@@ -6,6 +6,8 @@ from frappe.utils import add_days, flt, nowdate
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_delivery_note
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.report.gross_profit.gross_profit import execute
+from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
+from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
from erpnext.stock.doctype.item.test_item import create_item
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
@@ -14,6 +16,7 @@ class TestGrossProfit(FrappeTestCase):
def setUp(self):
self.create_company()
self.create_item()
+ self.create_bundle()
self.create_customer()
self.create_sales_invoice()
self.clear_old_entries()
@@ -42,6 +45,7 @@ class TestGrossProfit(FrappeTestCase):
self.company = company.name
self.cost_center = company.cost_center
self.warehouse = "Stores - " + abbr
+ self.finished_warehouse = "Finished Goods - " + abbr
self.income_account = "Sales - " + abbr
self.expense_account = "Cost of Goods Sold - " + abbr
self.debit_to = "Debtors - " + abbr
@@ -53,6 +57,23 @@ class TestGrossProfit(FrappeTestCase):
)
self.item = item if isinstance(item, str) else item.item_code
+ def create_bundle(self):
+ from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
+
+ item2 = create_item(
+ item_code="_Test GP Item 2", is_stock_item=1, company=self.company, warehouse=self.warehouse
+ )
+ self.item2 = item2 if isinstance(item2, str) else item2.item_code
+
+ # This will be parent item
+ bundle = create_item(
+ item_code="_Test GP bundle", is_stock_item=0, company=self.company, warehouse=self.warehouse
+ )
+ self.bundle = bundle if isinstance(bundle, str) else bundle.item_code
+
+ # Create Product Bundle
+ self.product_bundle = make_product_bundle(parent=self.bundle, items=[self.item, self.item2])
+
def create_customer(self):
name = "_Test GP Customer"
if frappe.db.exists("Customer", name):
@@ -93,6 +114,28 @@ class TestGrossProfit(FrappeTestCase):
)
return sinv
+ def create_delivery_note(
+ self, item=None, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False
+ ):
+ """
+ Helper function to populate default values in Delivery Note
+ """
+ dnote = create_delivery_note(
+ company=self.company,
+ customer=self.customer,
+ currency="INR",
+ item=item or self.item,
+ qty=qty,
+ rate=rate,
+ cost_center=self.cost_center,
+ warehouse=self.warehouse,
+ return_against=None,
+ expense_account=self.expense_account,
+ do_not_save=do_not_save,
+ do_not_submit=do_not_submit,
+ )
+ return dnote
+
def clear_old_entries(self):
doctype_list = [
"Sales Invoice",
@@ -206,3 +249,55 @@ class TestGrossProfit(FrappeTestCase):
}
gp_entry = [x for x in data if x.parent_invoice == sinv.name]
self.assertDictContainsSubset(expected_entry_with_dn, gp_entry[0])
+
+ def test_bundled_delivery_note_with_different_warehouses(self):
+ """
+ Test Delivery Note with bundled item. Packed Item from the bundle having different warehouses
+ """
+ se = make_stock_entry(
+ company=self.company,
+ item_code=self.item,
+ target=self.warehouse,
+ qty=1,
+ basic_rate=100,
+ do_not_submit=True,
+ )
+ item = se.items[0]
+ se.append(
+ "items",
+ {
+ "item_code": self.item2,
+ "s_warehouse": "",
+ "t_warehouse": self.finished_warehouse,
+ "qty": 1,
+ "basic_rate": 100,
+ "conversion_factor": item.conversion_factor or 1.0,
+ "transfer_qty": flt(item.qty) * (flt(item.conversion_factor) or 1.0),
+ "serial_no": item.serial_no,
+ "batch_no": item.batch_no,
+ "cost_center": item.cost_center,
+ "expense_account": item.expense_account,
+ },
+ )
+ se = se.save().submit()
+
+ # Make a Delivery note with Product bundle
+ # Packed Items will have different warehouses
+ dnote = self.create_delivery_note(item=self.bundle, qty=1, rate=200, do_not_submit=True)
+ dnote.packed_items[1].warehouse = self.finished_warehouse
+ dnote = dnote.submit()
+
+ # make Sales Invoice for above delivery note
+ sinv = make_sales_invoice(dnote.name)
+ sinv = sinv.save().submit()
+
+ filters = frappe._dict(
+ company=self.company,
+ from_date=nowdate(),
+ to_date=nowdate(),
+ group_by="Invoice",
+ sales_invoice=sinv.name,
+ )
+
+ columns, data = execute(filters=filters)
+ self.assertGreater(len(data), 0)
diff --git a/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js b/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js
index f81297760ed..5dc4c3d1c15 100644
--- a/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js
+++ b/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js
@@ -63,24 +63,6 @@ frappe.query_reports["Supplier Ledger Summary"] = {
"fieldtype": "Link",
"options": "Payment Terms Template"
},
- {
- "fieldname":"territory",
- "label": __("Territory"),
- "fieldtype": "Link",
- "options": "Territory"
- },
- {
- "fieldname":"sales_partner",
- "label": __("Sales Partner"),
- "fieldtype": "Link",
- "options": "Sales Partner"
- },
- {
- "fieldname":"sales_person",
- "label": __("Sales Person"),
- "fieldtype": "Link",
- "options": "Sales Person"
- },
{
"fieldname":"tax_id",
"label": __("Tax Id"),
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index 2144ae00366..6358896e7dc 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -370,7 +370,7 @@
{
"fieldname": "shipping_address",
"fieldtype": "Link",
- "label": "Company Shipping Address",
+ "label": "Shipping Address",
"options": "Address",
"print_hide": 1
},
@@ -1170,7 +1170,7 @@
"idx": 105,
"is_submittable": 1,
"links": [],
- "modified": "2022-11-17 12:34:36.033363",
+ "modified": "2022-12-25 18:08:59.074182",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",
diff --git a/erpnext/patches/v11_0/create_salary_structure_assignments.py b/erpnext/patches/v11_0/create_salary_structure_assignments.py
index b81e867b9dd..51b2a2cc0b1 100644
--- a/erpnext/patches/v11_0/create_salary_structure_assignments.py
+++ b/erpnext/patches/v11_0/create_salary_structure_assignments.py
@@ -13,8 +13,10 @@ from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assign
def execute():
+ frappe.reload_doc("Payroll", "doctype", "Payroll Settings")
frappe.reload_doc("Payroll", "doctype", "Salary Structure")
frappe.reload_doc("Payroll", "doctype", "Salary Structure Assignment")
+
frappe.db.sql(
"""
delete from `tabSalary Structure Assignment`
diff --git a/erpnext/payroll/doctype/payroll_settings/payroll_settings.json b/erpnext/payroll/doctype/payroll_settings/payroll_settings.json
index 54377e94b30..f4db6f099a6 100644
--- a/erpnext/payroll/doctype/payroll_settings/payroll_settings.json
+++ b/erpnext/payroll/doctype/payroll_settings/payroll_settings.json
@@ -11,6 +11,7 @@
"max_working_hours_against_timesheet",
"include_holidays_in_total_working_days",
"disable_rounded_total",
+ "define_opening_balance_for_earning_and_deductions",
"column_break_11",
"daily_wages_fraction_for_half_day",
"email_salary_slip_to_employee",
@@ -91,13 +92,20 @@
"fieldname": "show_leave_balances_in_salary_slip",
"fieldtype": "Check",
"label": "Show Leave Balances in Salary Slip"
+ },
+ {
+ "default": "0",
+ "description": "If checked, then the system will enable the provision to set the opening balance for earnings and deductions till date while creating a Salary Structure Assignment (if any)",
+ "fieldname": "define_opening_balance_for_earning_and_deductions",
+ "fieldtype": "Check",
+ "label": "Define Opening Balance for Earning and Deductions"
}
],
"icon": "fa fa-cog",
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2021-03-03 17:49:59.579723",
+ "modified": "2022-12-21 17:30:08.704247",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Payroll Settings",
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py
index b2243838202..0563541eaa5 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py
@@ -1063,7 +1063,25 @@ class SalarySlip(TransactionBase):
)
exempted_amount = flt(exempted_amount[0][0]) if exempted_amount else 0
- return taxable_earnings - exempted_amount
+ opening_taxable_earning = self.get_opening_balance_for(
+ "taxable_earnings_till_date", start_date, end_date
+ )
+
+ return (taxable_earnings + opening_taxable_earning) - exempted_amount
+
+ def get_opening_balance_for(self, field_to_select, start_date, end_date):
+ opening_balance = frappe.db.get_all(
+ "Salary Structure Assignment",
+ {
+ "employee": self.employee,
+ "salary_structure": self.salary_structure,
+ "from_date": ["between", (start_date, end_date)],
+ "docstatus": 1,
+ },
+ field_to_select,
+ )
+
+ return opening_balance[0].get(field_to_select) if opening_balance else 0.0
def get_tax_paid_in_period(self, start_date, end_date, tax_component):
# find total_tax_paid, tax paid for benefit, additional_salary
@@ -1092,7 +1110,11 @@ class SalarySlip(TransactionBase):
)[0][0]
)
- return total_tax_paid
+ tax_deducted_till_date = self.get_opening_balance_for(
+ "tax_deducted_till_date", start_date, end_date
+ )
+
+ return total_tax_paid + tax_deducted_till_date
def get_taxable_earnings(
self, allow_tax_exemption=False, based_on_payment_days=0, payroll_period=None
diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
index 6e3b57239d4..32d0c7ed08f 100644
--- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
@@ -1030,6 +1030,104 @@ class TestSalarySlip(FrappeTestCase):
activity_type.wage_rate = 25
activity_type.save()
+ def test_salary_slip_generation_against_opening_entries_in_ssa(self):
+ import math
+
+ from erpnext.payroll.doctype.payroll_period.payroll_period import get_period_factor
+ from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure
+
+ payroll_period = frappe.db.get_value(
+ "Payroll Period",
+ {
+ "company": "_Test Company",
+ "start_date": ["<=", "2023-03-31"],
+ "end_date": [">=", "2022-04-01"],
+ },
+ "name",
+ )
+
+ if not payroll_period:
+ payroll_period = create_payroll_period(
+ name="_Test Payroll Period for Tax",
+ company="_Test Company",
+ start_date="2022-04-01",
+ end_date="2023-03-31",
+ )
+ else:
+ payroll_period = frappe.get_cached_doc("Payroll Period", payroll_period)
+
+ emp = make_employee(
+ "test_employee_ss_with_opening_balance@salary.com",
+ company="_Test Company",
+ **{"date_of_joining": "2021-12-01"},
+ )
+ employee_doc = frappe.get_doc("Employee", emp)
+
+ create_tax_slab(payroll_period, allow_tax_exemption=True)
+
+ salary_structure_name = "Test Salary Structure for Opening Balance"
+ if not frappe.db.exists("Salary Structure", salary_structure_name):
+ salary_structure_doc = make_salary_structure(
+ salary_structure_name,
+ "Monthly",
+ company="_Test Company",
+ employee=emp,
+ from_date="2022-04-01",
+ payroll_period=payroll_period,
+ test_tax=True,
+ )
+
+ # validate no salary slip exists for the employee
+ self.assertTrue(
+ frappe.db.count(
+ "Salary Slip",
+ {
+ "employee": emp,
+ "salary_structure": salary_structure_doc.name,
+ "docstatus": 1,
+ "start_date": [">=", "2022-04-01"],
+ },
+ )
+ == 0
+ )
+
+ remaining_sub_periods = get_period_factor(
+ emp,
+ get_first_day("2022-10-01"),
+ get_last_day("2022-10-01"),
+ "Monthly",
+ payroll_period,
+ depends_on_payment_days=0,
+ )[1]
+
+ prev_period = math.ceil(remaining_sub_periods)
+
+ annual_tax = 93036 # 89220 #data[0].get('applicable_tax')
+ monthly_tax_amount = 7732.40 # 7435 #annual_tax/12
+ annual_earnings = 933600 # data[0].get('ctc')
+ monthly_earnings = 77800 # annual_earnings/12
+
+ # Get Salary Structure Assignment
+ ssa = frappe.get_value(
+ "Salary Structure Assignment",
+ {"employee": emp, "salary_structure": salary_structure_doc.name},
+ "name",
+ )
+ ssa_doc = frappe.get_doc("Salary Structure Assignment", ssa)
+
+ # Set opening balance for earning and tax deduction in Salary Structure Assignment
+ ssa_doc.taxable_earnings_till_date = monthly_earnings * prev_period
+ ssa_doc.tax_deducted_till_date = monthly_tax_amount * prev_period
+ ssa_doc.save()
+
+ # Create Salary Slip
+ salary_slip = make_salary_slip(
+ salary_structure_doc.name, employee=employee_doc.name, posting_date=getdate("2022-10-01")
+ )
+ for deduction in salary_slip.deductions:
+ if deduction.salary_component == "TDS":
+ self.assertEqual(deduction.amount, rounded(monthly_tax_amount))
+
def get_no_of_days():
no_of_days_in_month = calendar.monthrange(getdate(nowdate()).year, getdate(nowdate()).month)
diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js
index 6cd897e95d1..7cb573d6307 100644
--- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js
+++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.js
@@ -42,6 +42,13 @@ frappe.ui.form.on('Salary Structure Assignment', {
});
},
+ refresh: function(frm) {
+ if(frm.doc.__onload){
+ frm.unhide_earnings_and_taxation_section = frm.doc.__onload.earning_and_deduction_entries_does_not_exists;
+ frm.trigger("set_earnings_and_taxation_section_visibility");
+ }
+ },
+
employee: function(frm) {
if(frm.doc.employee){
frappe.call({
@@ -59,6 +66,8 @@ frappe.ui.form.on('Salary Structure Assignment', {
}
}
});
+
+ frm.trigger("valiadte_joining_date_and_salary_slips");
}
else{
frm.set_value("company", null);
@@ -71,5 +80,33 @@ frappe.ui.form.on('Salary Structure Assignment', {
frm.set_value("payroll_payable_account", r.default_payroll_payable_account);
});
}
- }
+ },
+
+ valiadte_joining_date_and_salary_slips: function(frm) {
+ frappe.call({
+ method: "earning_and_deduction_entries_does_not_exists",
+ doc: frm.doc,
+ callback: function(data) {
+ let earning_and_deduction_entries_does_not_exists = data.message;
+ frm.unhide_earnings_and_taxation_section = earning_and_deduction_entries_does_not_exists;
+ frm.trigger("set_earnings_and_taxation_section_visibility");
+ }
+ });
+ },
+
+ set_earnings_and_taxation_section_visibility: function(frm) {
+ if(frm.unhide_earnings_and_taxation_section){
+ frm.set_df_property('earnings_and_taxation_section', 'hidden', 0);
+ }
+ else{
+ frm.set_df_property('earnings_and_taxation_section', 'hidden', 1);
+ }
+ },
+
+ from_date: function(frm) {
+ if (frm.doc.from_date) {
+ frm.trigger("valiadte_joining_date_and_salary_slips" );
+ }
+ },
+
});
diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json
index c8b98e5aafc..4db023c6d51 100644
--- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json
+++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.json
@@ -22,6 +22,10 @@
"base",
"column_break_9",
"variable",
+ "earnings_and_taxation_section",
+ "taxable_earnings_till_date",
+ "column_break_18",
+ "tax_deducted_till_date",
"amended_from"
],
"fields": [
@@ -141,11 +145,31 @@
"fieldtype": "Link",
"label": "Payroll Payable Account",
"options": "Account"
+ },
+ {
+ "fieldname": "earnings_and_taxation_section",
+ "fieldtype": "Section Break"
+ },
+ {
+ "allow_on_submit": 1,
+ "fieldname": "tax_deducted_till_date",
+ "fieldtype": "Currency",
+ "label": "Tax Deducted Till Date"
+ },
+ {
+ "fieldname": "column_break_18",
+ "fieldtype": "Column Break"
+ },
+ {
+ "allow_on_submit": 1,
+ "fieldname": "taxable_earnings_till_date",
+ "fieldtype": "Currency",
+ "label": "Taxable Earnings Till Date"
}
],
"is_submittable": 1,
"links": [],
- "modified": "2021-03-31 22:44:46.267974",
+ "modified": "2022-12-26 12:47:42.521891",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Salary Structure Assignment",
diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py
index e34e48e6c05..21196c3e52d 100644
--- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py
+++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py
@@ -13,10 +13,32 @@ class DuplicateAssignment(frappe.ValidationError):
class SalaryStructureAssignment(Document):
+ def onload(self):
+ if self.employee:
+ self.set_onload(
+ "earning_and_deduction_entries_exists", self.earning_and_deduction_entries_does_not_exists()
+ )
+
def validate(self):
self.validate_dates()
self.validate_income_tax_slab()
self.set_payroll_payable_account()
+ self.valiadte_missing_taxable_earnings_and_deductions_till_date()
+
+ def valiadte_missing_taxable_earnings_and_deductions_till_date(self):
+ if self.earning_and_deduction_entries_does_not_exists():
+ if not self.taxable_earnings_till_date and not self.tax_deducted_till_date:
+ frappe.msgprint(
+ _(
+ """Not found any salary slip record(s) for the employee {0}.
Please specify {1} and {2} (if any), for the correct tax calculation in future salary slips."""
+ ).format(
+ self.employee,
+ "" + _("Taxable Earnings Till Date") + "",
+ "" + _("Tax Deducted Till Date") + "",
+ ),
+ indicator="orange",
+ title=_("Warning"),
+ )
def validate_dates(self):
joining_date, relieving_date = frappe.db.get_value(
@@ -76,6 +98,56 @@ class SalaryStructureAssignment(Document):
)
self.payroll_payable_account = payroll_payable_account
+ @frappe.whitelist()
+ def earning_and_deduction_entries_does_not_exists(self):
+ if self.enabled_settings_to_specify_earnings_and_deductions_till_date():
+ if not self.joined_in_the_same_month() and not self.have_salary_slips():
+ return True
+ else:
+ if self.docstatus in [1, 2] and (
+ self.taxable_earnings_till_date or self.tax_deducted_till_date
+ ):
+ return True
+ return False
+ else:
+ return False
+
+ def enabled_settings_to_specify_earnings_and_deductions_till_date(self):
+ """returns True if settings are enabled to specify earnings and deductions till date else False"""
+
+ if frappe.db.get_single_value(
+ "Payroll Settings", "define_opening_balance_for_earning_and_deductions"
+ ):
+ return True
+ return False
+
+ def have_salary_slips(self):
+ """returns True if salary structure assignment has salary slips else False"""
+
+ salary_slip = frappe.db.get_value(
+ "Salary Slip", filters={"employee": self.employee, "docstatus": 1}
+ )
+
+ if salary_slip:
+ return True
+
+ return False
+
+ def joined_in_the_same_month(self):
+ """returns True if employee joined in same month as salary structure assignment from date else False"""
+
+ date_of_joining = frappe.db.get_value("Employee", self.employee, "date_of_joining")
+ from_date = getdate(self.from_date)
+
+ if not self.from_date or not date_of_joining:
+ return False
+
+ elif date_of_joining.month == from_date.month:
+ return True
+
+ else:
+ return False
+
def get_assigned_salary_structure(employee, on_date):
if not employee or not on_date:
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 303e4fff4af..8b28e02797d 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -1090,6 +1090,15 @@ def make_purchase_order(source_name, selected_items=None, target_doc=None):
]
items_to_map = list(set(items_to_map))
+ def is_drop_ship_order(target):
+ drop_ship = True
+ for item in target.items:
+ if not item.delivered_by_supplier:
+ drop_ship = False
+ break
+
+ return drop_ship
+
def set_missing_values(source, target):
target.supplier = ""
target.apply_discount_on = ""
@@ -1097,8 +1106,14 @@ def make_purchase_order(source_name, selected_items=None, target_doc=None):
target.discount_amount = 0.0
target.inter_company_order_reference = ""
target.shipping_rule = ""
- target.customer = ""
- target.customer_name = ""
+
+ if is_drop_ship_order(target):
+ target.customer = source.customer
+ target.customer_name = source.customer_name
+ target.shipping_address = source.shipping_address_name
+ else:
+ target.customer = target.customer_name = target.shipping_address = None
+
target.run_method("set_missing_values")
target.run_method("calculate_taxes_and_totals")
diff --git a/erpnext/setup/doctype/customer_group/customer_group.json b/erpnext/setup/doctype/customer_group/customer_group.json
index 0e2ed9efcf8..d6a431ea616 100644
--- a/erpnext/setup/doctype/customer_group/customer_group.json
+++ b/erpnext/setup/doctype/customer_group/customer_group.json
@@ -139,10 +139,11 @@
"idx": 1,
"is_tree": 1,
"links": [],
- "modified": "2021-02-08 17:01:52.162202",
+ "modified": "2022-12-24 11:15:17.142746",
"modified_by": "Administrator",
"module": "Setup",
"name": "Customer Group",
+ "naming_rule": "By fieldname",
"nsm_parent_field": "parent_customer_group",
"owner": "Administrator",
"permissions": [
@@ -198,10 +199,19 @@
"role": "Customer",
"select": 1,
"share": 1
+ },
+ {
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts User",
+ "share": 1
}
],
"search_fields": "parent_customer_group",
"show_name_in_global_search": 1,
"sort_field": "modified",
- "sort_order": "DESC"
+ "sort_order": "DESC",
+ "states": []
}
\ No newline at end of file
diff --git a/erpnext/setup/doctype/supplier_group/supplier_group.json b/erpnext/setup/doctype/supplier_group/supplier_group.json
index 9119bb947cb..b3ed608cd03 100644
--- a/erpnext/setup/doctype/supplier_group/supplier_group.json
+++ b/erpnext/setup/doctype/supplier_group/supplier_group.json
@@ -6,6 +6,7 @@
"creation": "2013-01-10 16:34:24",
"doctype": "DocType",
"document_type": "Setup",
+ "engine": "InnoDB",
"field_order": [
"supplier_group_name",
"parent_supplier_group",
@@ -106,10 +107,11 @@
"idx": 1,
"is_tree": 1,
"links": [],
- "modified": "2020-03-18 18:10:49.228407",
+ "modified": "2022-12-24 11:16:12.486719",
"modified_by": "Administrator",
"module": "Setup",
"name": "Supplier Group",
+ "naming_rule": "By fieldname",
"nsm_parent_field": "parent_supplier_group",
"owner": "Administrator",
"permissions": [
@@ -156,8 +158,18 @@
"permlevel": 1,
"read": 1,
"role": "Purchase User"
+ },
+ {
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts User",
+ "share": 1
}
],
"show_name_in_global_search": 1,
- "sort_order": "ASC"
+ "sort_field": "modified",
+ "sort_order": "ASC",
+ "states": []
}
\ No newline at end of file
diff --git a/erpnext/setup/doctype/territory/territory.json b/erpnext/setup/doctype/territory/territory.json
index a25bda054b9..c3a49933746 100644
--- a/erpnext/setup/doctype/territory/territory.json
+++ b/erpnext/setup/doctype/territory/territory.json
@@ -123,11 +123,12 @@
"idx": 1,
"is_tree": 1,
"links": [],
- "modified": "2021-02-08 17:10:03.767426",
+ "modified": "2022-12-24 11:16:39.964956",
"modified_by": "Administrator",
"module": "Setup",
"name": "Territory",
"name_case": "Title Case",
+ "naming_rule": "By fieldname",
"nsm_parent_field": "parent_territory",
"owner": "Administrator",
"permissions": [
@@ -175,10 +176,19 @@
"role": "Customer",
"select": 1,
"share": 1
+ },
+ {
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts User",
+ "share": 1
}
],
"search_fields": "parent_territory,territory_manager",
"show_name_in_global_search": 1,
"sort_field": "modified",
- "sort_order": "DESC"
+ "sort_order": "DESC",
+ "states": []
}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index d53f4cdbac9..b27abae513e 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -10,7 +10,6 @@ import frappe
from frappe import _
from frappe.model.mapper import get_mapped_doc
from frappe.query_builder.functions import Sum
-from six import iteritems, itervalues, string_types
from frappe.utils import (
add_days,
cint,
@@ -23,6 +22,7 @@ from frappe.utils import (
nowdate,
today,
)
+from six import iteritems, itervalues, string_types
import erpnext
from erpnext.accounts.general_ledger import process_gl_map
@@ -2625,4 +2625,3 @@ def get_incorrect_stock_entries() -> Dict:
stock_entries.setdefault(row.name, row)
return stock_entries
-