mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-21 14:09:19 +00:00
feat: Sales Commissoion Payout
This commit is contained in:
@@ -283,7 +283,7 @@ class PaymentEntry(AccountsController):
|
|||||||
elif self.party_type == "Supplier":
|
elif self.party_type == "Supplier":
|
||||||
valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry")
|
valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry")
|
||||||
elif self.party_type == "Employee":
|
elif self.party_type == "Employee":
|
||||||
valid_reference_doctypes = ("Expense Claim", "Journal Entry", "Employee Advance", "Gratuity")
|
valid_reference_doctypes = ("Expense Claim", "Journal Entry", "Employee Advance", "Gratuity", "Sales Commission")
|
||||||
elif self.party_type == "Shareholder":
|
elif self.party_type == "Shareholder":
|
||||||
valid_reference_doctypes = ("Journal Entry")
|
valid_reference_doctypes = ("Journal Entry")
|
||||||
elif self.party_type == "Donor":
|
elif self.party_type == "Donor":
|
||||||
@@ -1355,7 +1355,10 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
|
|||||||
exchange_rate = 1
|
exchange_rate = 1
|
||||||
outstanding_amount = get_outstanding_on_journal_entry(reference_name)
|
outstanding_amount = get_outstanding_on_journal_entry(reference_name)
|
||||||
elif reference_doctype != "Journal Entry":
|
elif reference_doctype != "Journal Entry":
|
||||||
if ref_doc.doctype == "Expense Claim":
|
if ref_doc.doctype == "Sales Commission":
|
||||||
|
total_amount = ref_doc.total_commission_amount
|
||||||
|
exchange_rate = 1
|
||||||
|
elif ref_doc.doctype == "Expense Claim":
|
||||||
total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges)
|
total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges)
|
||||||
elif ref_doc.doctype == "Employee Advance":
|
elif ref_doc.doctype == "Employee Advance":
|
||||||
total_amount = ref_doc.advance_amount
|
total_amount = ref_doc.advance_amount
|
||||||
@@ -1389,6 +1392,8 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
|
|||||||
exchange_rate = 1
|
exchange_rate = 1
|
||||||
elif reference_doctype == "Gratuity":
|
elif reference_doctype == "Gratuity":
|
||||||
outstanding_amount = ref_doc.amount - flt(ref_doc.paid_amount)
|
outstanding_amount = ref_doc.amount - flt(ref_doc.paid_amount)
|
||||||
|
elif reference_doctype == "Sales Commission":
|
||||||
|
outstanding_amount = 0
|
||||||
else:
|
else:
|
||||||
outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid)
|
outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid)
|
||||||
else:
|
else:
|
||||||
|
|||||||
0
erpnext/payroll/doctype/contributions/__init__.py
Normal file
0
erpnext/payroll/doctype/contributions/__init__.py
Normal file
101
erpnext/payroll/doctype/contributions/contributions.json
Normal file
101
erpnext/payroll/doctype/contributions/contributions.json
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"allow_rename": 1,
|
||||||
|
"creation": "2021-09-07 12:49:18.526652",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"document_type",
|
||||||
|
"order_or_invoice",
|
||||||
|
"customer",
|
||||||
|
"customer_name",
|
||||||
|
"posting_date",
|
||||||
|
"column_break_5",
|
||||||
|
"contribution_percent",
|
||||||
|
"contribution_amount",
|
||||||
|
"commission_rate",
|
||||||
|
"commission_amount"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "order_or_invoice",
|
||||||
|
"fieldtype": "Dynamic Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Order / Invoice",
|
||||||
|
"options": "document_type",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "customer",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Customer",
|
||||||
|
"options": "Customer",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "customer.customer_name",
|
||||||
|
"fieldname": "customer_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Customer Name",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "posting_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Posting Date",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "commission_rate",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Commission Rate",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "commission_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Commission Amount",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_5",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "document_type",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "Document Type",
|
||||||
|
"options": "DocType"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "contribution_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Contribution Amount",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "contribution_percent",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Contribution %",
|
||||||
|
"read_only": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"istable": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2021-09-13 19:11:43.548342",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Payroll",
|
||||||
|
"name": "Contributions",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 1
|
||||||
|
}
|
||||||
9
erpnext/payroll/doctype/contributions/contributions.py
Normal file
9
erpnext/payroll/doctype/contributions/contributions.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
|
class Contributions(Document):
|
||||||
|
pass
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
"payroll_based_on",
|
"payroll_based_on",
|
||||||
"consider_unmarked_attendance_as",
|
"consider_unmarked_attendance_as",
|
||||||
"max_working_hours_against_timesheet",
|
"max_working_hours_against_timesheet",
|
||||||
|
"salary_component_for_sales_commission",
|
||||||
"include_holidays_in_total_working_days",
|
"include_holidays_in_total_working_days",
|
||||||
"disable_rounded_total",
|
"disable_rounded_total",
|
||||||
"column_break_11",
|
"column_break_11",
|
||||||
@@ -91,13 +92,19 @@
|
|||||||
"fieldname": "show_leave_balances_in_salary_slip",
|
"fieldname": "show_leave_balances_in_salary_slip",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Show Leave Balances in Salary Slip"
|
"label": "Show Leave Balances in Salary Slip"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "salary_component_for_sales_commission",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Salary Component for Sales Commission",
|
||||||
|
"options": "Salary Component"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-cog",
|
"icon": "fa fa-cog",
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-03-03 17:49:59.579723",
|
"modified": "2021-09-07 12:21:16.640474",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Payroll",
|
"module": "Payroll",
|
||||||
"name": "Payroll Settings",
|
"name": "Payroll Settings",
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on('Process Sales Commission', {
|
||||||
|
setup: function(frm){
|
||||||
|
frm.set_query("department", function() {
|
||||||
|
if (!frm.doc.company) {
|
||||||
|
frappe.throw(__("Please select company first"))
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
company: frm.doc.company
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"autoname": "format:PRO-SAL-COM-{#####}",
|
||||||
|
"creation": "2021-09-08 13:28:11.658071",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"company",
|
||||||
|
"department",
|
||||||
|
"column_break_3",
|
||||||
|
"designation",
|
||||||
|
"branch",
|
||||||
|
"section_break_6",
|
||||||
|
"from_date",
|
||||||
|
"to_date",
|
||||||
|
"column_break_9",
|
||||||
|
"commission_based_on",
|
||||||
|
"pay_via_salary",
|
||||||
|
"amended_from"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "company",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Company",
|
||||||
|
"options": "Company",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "department",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Department",
|
||||||
|
"options": "Department"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_3",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "designation",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Designation",
|
||||||
|
"options": "Designation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "branch",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Branch",
|
||||||
|
"options": "Branch"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_6",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "from_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "From Date",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "to_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "To Date",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_9",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "commission_based_on",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Commission Based on",
|
||||||
|
"options": "Sales Order\nSales Invoice"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "pay_via_salary",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Pay Via Salary"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "amended_from",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Amended From",
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "Process Sales Commission",
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"is_submittable": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2021-09-20 15:45:39.240487",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Payroll",
|
||||||
|
"name": "Process Sales Commission",
|
||||||
|
"naming_rule": "Expression",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "System Manager",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Sales Manager",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "HR Manager",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
from frappe.model.document import Document
|
||||||
|
from frappe.utils import get_link_to_form
|
||||||
|
|
||||||
|
|
||||||
|
class ProcessSalesCommission(Document):
|
||||||
|
def validate(self):
|
||||||
|
self.validate_from_to_dates()
|
||||||
|
self.validate_salary_component()
|
||||||
|
|
||||||
|
def validate_from_to_dates(self):
|
||||||
|
return super().validate_from_to_dates("from_date", "to_date")
|
||||||
|
|
||||||
|
def validate_salary_component(self):
|
||||||
|
if self.pay_via_salary:
|
||||||
|
if not frappe.db.get_single_value("Payroll Settings", "salary_component_for_sales_commission"):
|
||||||
|
frappe.throw(_("Please set {0} in {1}").format(frappe.bold("Salary Component for Sales Commission"), get_link_to_form("Payroll Settings", "Payroll Settings")))
|
||||||
|
|
||||||
|
def on_submit(self):
|
||||||
|
self.process_sales_commission()
|
||||||
|
|
||||||
|
def process_sales_commission(self):
|
||||||
|
filter_date = "transaction_date" if self.commission_based_on=="Sales Order" else "posting_date"
|
||||||
|
records = [entry.name for entry in frappe.db.get_all(self.commission_based_on, filters={ "company": self.company, filter_date: ('between', [self.from_date, self.to_date])})]
|
||||||
|
sales_persons_details = frappe.get_all("Sales Team", filters={"parent": ['in', records]}, fields=["sales_person", "commission_rate", "incentives", "allocated_percentage", "allocated_amount", "parent"])
|
||||||
|
if len(sales_persons_details):
|
||||||
|
sales_persons = set(e['sales_person'] for e in sales_persons_details)
|
||||||
|
sales_persons_list = self.get_sales_persons_list(sales_persons)
|
||||||
|
sales_persons_details_map = self.map_sales_persons_details(sales_persons_list, sales_persons_details)
|
||||||
|
self.make_sales_commission_document(sales_persons_details_map, filter_date)
|
||||||
|
|
||||||
|
def get_sales_persons_list(self, sales_persons):
|
||||||
|
sales_persons_list = sales_persons
|
||||||
|
if self.department or self.designation or self.branch:
|
||||||
|
for person in sales_persons:
|
||||||
|
emp = frappe.db.get_value("Sales Person", filters={"name":person}, fieldname="employee", as_dict=True)['employee']
|
||||||
|
if emp:
|
||||||
|
employee_details = frappe.db.get_value("Employee", filters={"name":emp}, fieldname=["company", "department", "designation", "branch"], as_dict=True)
|
||||||
|
if self.company != employee_details["company"]:
|
||||||
|
sales_persons_list.remove(person)
|
||||||
|
continue
|
||||||
|
if self.department and self.department != employee_details["department"]:
|
||||||
|
sales_persons_list.remove(person)
|
||||||
|
continue
|
||||||
|
if self.designation and self.designation != employee_details["designation"]:
|
||||||
|
sales_persons_list.remove(person)
|
||||||
|
continue
|
||||||
|
if self.branch and self.branch != employee_details["branch"]:
|
||||||
|
sales_persons_list.remove(person)
|
||||||
|
continue
|
||||||
|
|
||||||
|
return sales_persons_list
|
||||||
|
|
||||||
|
def map_sales_persons_details(self, sales_persons, sales_persons_details):
|
||||||
|
sales_persons_details_map = {}
|
||||||
|
for person in sales_persons:
|
||||||
|
sales_persons_details_map[person] = []
|
||||||
|
for details in sales_persons_details:
|
||||||
|
if details['sales_person'] == person:
|
||||||
|
sales_persons_details_map[person].append(details)
|
||||||
|
|
||||||
|
return sales_persons_details_map
|
||||||
|
|
||||||
|
def make_sales_commission_document(self, sales_persons_details_map, filter_date):
|
||||||
|
for record in sales_persons_details_map:
|
||||||
|
doc = doc = frappe.new_doc("Sales Commission")
|
||||||
|
doc.sales_person = record
|
||||||
|
doc.from_date = self.from_date
|
||||||
|
doc.to_date = self.to_date
|
||||||
|
doc.pay_via_salary = self.pay_via_salary
|
||||||
|
doc.process_sales_commission_reference = self.name
|
||||||
|
doc.set("contributions", [])
|
||||||
|
self.add_contributions(doc, sales_persons_details_map[record], filter_date)
|
||||||
|
doc.insert()
|
||||||
|
if not frappe.db.get_single_value("Selling Settings", "approval_required_for_sales_commission_payout"):
|
||||||
|
doc.reload()
|
||||||
|
if self.pay_via_salary and doc.employee:
|
||||||
|
if frappe.db.exists('Salary Structure Assignment', {'employee': doc.employee}):
|
||||||
|
doc.submit()
|
||||||
|
|
||||||
|
def add_contributions(self, doc, records, filter_date):
|
||||||
|
for items in records:
|
||||||
|
sales_record_details = frappe.db.get_value(self.commission_based_on, filters={"name": items["parent"]}, fieldname=["customer", filter_date], as_dict=True)
|
||||||
|
contribution = {
|
||||||
|
"document_type": self.commission_based_on,
|
||||||
|
"order_or_invoice": items["parent"],
|
||||||
|
"customer": sales_record_details["customer"],
|
||||||
|
"posting_date": sales_record_details[filter_date],
|
||||||
|
"contribution_percent": items["allocated_percentage"],
|
||||||
|
"contribution_amount": items["allocated_amount"],
|
||||||
|
"commission_rate": items["commission_rate"],
|
||||||
|
"commission_amount": items["incentives"],
|
||||||
|
}
|
||||||
|
doc.append("contributions", contribution)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# See license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TestProcessSalesCommission(unittest.TestCase):
|
||||||
|
pass
|
||||||
118
erpnext/payroll/doctype/sales_commission/sales_commission.js
Normal file
118
erpnext/payroll/doctype/sales_commission/sales_commission.js
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on('Sales Commission', {
|
||||||
|
setup: function(frm){
|
||||||
|
frm.set_query("commission_based_on", function() {
|
||||||
|
return {
|
||||||
|
filters: [
|
||||||
|
['name', 'in', ["Sales Order", "Sales Invoice"]]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
refresh: function(frm) {
|
||||||
|
if (frm.doc.docstatus == 1) {
|
||||||
|
if (frm.custom_buttons) frm.clear_custom_buttons();
|
||||||
|
frm.events.add_context_buttons(frm);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
add_context_buttons: function (frm) {
|
||||||
|
if (!frm.doc.reference_name) {
|
||||||
|
if (frm.doc.pay_via_salary) {
|
||||||
|
frm.add_custom_button(__("Create Additional Salary"), function () {
|
||||||
|
create_additional_salary(frm);
|
||||||
|
}).addClass("btn-primary");
|
||||||
|
} else {
|
||||||
|
frm.add_custom_button(__("Create Payment Entry"), function () {
|
||||||
|
create_payment_entry(frm);
|
||||||
|
}).addClass("btn-primary");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
const create_payment_entry = function (frm) {
|
||||||
|
var d = new frappe.ui.Dialog({
|
||||||
|
title: __("Select Mode of Payment"),
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
'fieldname': 'mode_of_payment',
|
||||||
|
'fieldtype': 'Link',
|
||||||
|
'label': __('Mode of Payment'),
|
||||||
|
'options': 'Mode of Payment',
|
||||||
|
"get_query": function () {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
type: ["in", ["Bank", "Cash"]]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
'reqd': 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
d.set_primary_action(__('Create'), function() {
|
||||||
|
d.hide();
|
||||||
|
var arg = d.get_values();
|
||||||
|
frappe.confirm(__("Creating Payment Entry. Do you want to proceed?"),
|
||||||
|
function () {
|
||||||
|
frappe.call({
|
||||||
|
method: 'payout_entry',
|
||||||
|
args: {
|
||||||
|
"mode_of_payment": arg.mode_of_payment
|
||||||
|
},
|
||||||
|
callback: function () {
|
||||||
|
frappe.set_route(
|
||||||
|
'Form', "Payment Entry", {
|
||||||
|
"Payment Entry Reference.reference_name": frm.doc.name
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
doc: frm.doc,
|
||||||
|
freeze: true,
|
||||||
|
freeze_message: __('Creating Payment Entry')
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function () {
|
||||||
|
if (frappe.dom.freeze_count) {
|
||||||
|
frappe.dom.unfreeze();
|
||||||
|
frm.events.refresh(frm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
d.show();
|
||||||
|
};
|
||||||
|
|
||||||
|
const create_additional_salary = function (frm) {
|
||||||
|
if (!frm.doc.employee) {
|
||||||
|
frappe.throw(__("No employee is linked to Sales Person {0}. Please select an employee for {1} to process this Commission.").format(frappe.bold(frm.doc.sales_person), get_link_to_form("Sales Person", frm.doc.sales_person)))
|
||||||
|
}
|
||||||
|
frappe.confirm(__("Creating Additional Salary. Do you want to proceed?"),
|
||||||
|
function () {
|
||||||
|
frappe.call({
|
||||||
|
method: 'payout_entry',
|
||||||
|
args: {},
|
||||||
|
callback: function () {
|
||||||
|
frappe.set_route(
|
||||||
|
"Form", "Additional Salary", {
|
||||||
|
"Additional Salary.ref_docname": frm.doc.name
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
doc: frm.doc,
|
||||||
|
freeze: true,
|
||||||
|
freeze_message: __('Creating Additional Salary')
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function () {
|
||||||
|
if (frappe.dom.freeze_count) {
|
||||||
|
frappe.dom.unfreeze();
|
||||||
|
frm.events.refresh(frm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
263
erpnext/payroll/doctype/sales_commission/sales_commission.json
Normal file
263
erpnext/payroll/doctype/sales_commission/sales_commission.json
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"autoname": "format:SAL-COM-{#####}",
|
||||||
|
"creation": "2021-09-07 12:43:03.200379",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"sales_person",
|
||||||
|
"employee",
|
||||||
|
"employee_name",
|
||||||
|
"designation",
|
||||||
|
"department",
|
||||||
|
"branch",
|
||||||
|
"column_break_6",
|
||||||
|
"status",
|
||||||
|
"company",
|
||||||
|
"pay_via_salary",
|
||||||
|
"section_break_10",
|
||||||
|
"from_date",
|
||||||
|
"to_date",
|
||||||
|
"column_break_13",
|
||||||
|
"commission_based_on",
|
||||||
|
"process_sales_commission_reference",
|
||||||
|
"section_break_15",
|
||||||
|
"contributions",
|
||||||
|
"section_break_17",
|
||||||
|
"total_contribution",
|
||||||
|
"total_commission_amount",
|
||||||
|
"remarks",
|
||||||
|
"column_break_21",
|
||||||
|
"commission_rate",
|
||||||
|
"calculate_commission_manually",
|
||||||
|
"amended_from",
|
||||||
|
"reference_doctype",
|
||||||
|
"reference_name"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fetch_from": "sales_person.employee",
|
||||||
|
"fieldname": "employee",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Employee",
|
||||||
|
"options": "Employee",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "employee",
|
||||||
|
"fetch_from": "employee.employee_name",
|
||||||
|
"fieldname": "employee_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Employee Name",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "employee.designation",
|
||||||
|
"fieldname": "designation",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Designation",
|
||||||
|
"options": "Designation",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "employee.department",
|
||||||
|
"fieldname": "department",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Department",
|
||||||
|
"options": "Department",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "employee.branch",
|
||||||
|
"fieldname": "branch",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Branch",
|
||||||
|
"options": "Branch",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "employee.company",
|
||||||
|
"fieldname": "company",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Company",
|
||||||
|
"options": "Company",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "status",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Status",
|
||||||
|
"options": "Draft\nUnpaid\nPaid",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "pay_via_salary",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Pay Via Salary"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "from_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "From Date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "to_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "To Date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "commission_based_on",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Commission Based on",
|
||||||
|
"options": "Sales Order\nSales Invoice"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "total_contribution",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Total Contribution",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "calculate_commission_manually",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Calculate Commission Manually"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "calculate_commission_manually",
|
||||||
|
"fieldname": "commission_rate",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Commission Rate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "total_commission_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Total Commission Amount",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "remarks",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "Remarks"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "amended_from",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Amended From",
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "Sales Commission",
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "contributions",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"label": "Contributions",
|
||||||
|
"options": "Contributions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_6",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_10",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_13",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_15",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_17",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_21",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "process_sales_commission_reference",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Process Sales Commission Reference",
|
||||||
|
"options": "Process Sales Commission",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "sales_person",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Sales Person",
|
||||||
|
"options": "Sales Person"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "reference_doctype",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "Reference Doctype",
|
||||||
|
"options": "DocType"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "reference_name",
|
||||||
|
"fieldtype": "Dynamic Link",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "Reference Name",
|
||||||
|
"options": "reference_doctype"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"is_submittable": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2021-09-20 15:46:26.805073",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Payroll",
|
||||||
|
"name": "Sales Commission",
|
||||||
|
"naming_rule": "Expression",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "System Manager",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Sales Manager",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "HR Manager",
|
||||||
|
"share": 1,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 1
|
||||||
|
}
|
||||||
103
erpnext/payroll/doctype/sales_commission/sales_commission.py
Normal file
103
erpnext/payroll/doctype/sales_commission/sales_commission.py
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
from frappe.model.document import Document
|
||||||
|
from frappe.utils import get_link_to_form
|
||||||
|
|
||||||
|
|
||||||
|
class SalesCommission(Document):
|
||||||
|
def validate(self):
|
||||||
|
self.validate_from_to_dates()
|
||||||
|
self.validate_salary_component()
|
||||||
|
self.calculate_total_contribution_and_total_commission_amount()
|
||||||
|
|
||||||
|
def validate_from_to_dates(self):
|
||||||
|
return super().validate_from_to_dates("from_date", "to_date")
|
||||||
|
|
||||||
|
def validate_salary_component(self):
|
||||||
|
if self.pay_via_salary:
|
||||||
|
if not frappe.db.get_single_value("Payroll Settings", "salary_component_for_sales_commission"):
|
||||||
|
frappe.throw(_("Please set {0} in {1}").format(frappe.bold("Salary Component for Sales Commission"), get_link_to_form("Payroll Settings", "Payroll Settings")))
|
||||||
|
|
||||||
|
def calculate_total_contribution_and_total_commission_amount(self):
|
||||||
|
total_contribution, total_commission_amount = 0,0
|
||||||
|
for entry in self.contributions:
|
||||||
|
total_contribution += entry.contribution_amount
|
||||||
|
total_commission_amount += entry.commission_amount
|
||||||
|
|
||||||
|
if self.calculate_commission_manually:
|
||||||
|
rate = self.commission_rate
|
||||||
|
total_commission_amount = total_contribution * (rate / 100)
|
||||||
|
|
||||||
|
self.total_contribution = total_contribution
|
||||||
|
self.total_commission_amount = total_commission_amount
|
||||||
|
|
||||||
|
def on_submit(self):
|
||||||
|
if not self.employee:
|
||||||
|
frappe.throw(_("No employee is linked to Sales Person: {0}. Please select an employee for {1} to submit this document.").format(frappe.bold(self.sales_person), get_link_to_form("Sales Person", self.sales_person)))
|
||||||
|
if self.pay_via_salary:
|
||||||
|
self.make_additional_salary()
|
||||||
|
else:
|
||||||
|
self.make_payment_entry()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def payout_entry(self, mode_of_payment=None):
|
||||||
|
from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account
|
||||||
|
if mode_of_payment:
|
||||||
|
paid_from = get_bank_cash_account(mode_of_payment, self.company).get("account")
|
||||||
|
|
||||||
|
paid_to = frappe.db.get_value("Company", filters={"name":self.company}, fieldname=['default_payable_account'], as_dict=True)['default_payable_account']
|
||||||
|
if not paid_to:
|
||||||
|
frappe.throw(_("Please set Default Payable Account in {}").format(get_link_to_form("Company", self.company)))
|
||||||
|
if self.pay_via_salary:
|
||||||
|
self.make_additional_salary()
|
||||||
|
else:
|
||||||
|
self.make_payment_entry(mode_of_payment, paid_from, paid_to)
|
||||||
|
|
||||||
|
|
||||||
|
def make_additional_salary(self):
|
||||||
|
doc = frappe.new_doc("Additional Salary")
|
||||||
|
doc.employee = self.employee
|
||||||
|
doc.company = self.company
|
||||||
|
doc.salary_component = frappe.db.get_single_value("Payroll Settings", "salary_component_for_sales_commission")
|
||||||
|
doc.payroll_date = self.to_date
|
||||||
|
doc.amount = self.total_commission_amount
|
||||||
|
doc.ref_doctype = self.doctype
|
||||||
|
doc.ref_docname = self.name
|
||||||
|
|
||||||
|
doc.submit()
|
||||||
|
|
||||||
|
self.db_set("reference_doctype", "Additional Salary")
|
||||||
|
self.db_set("reference_name", doc.name)
|
||||||
|
|
||||||
|
def make_payment_entry(self, mode_of_payment, paid_from, paid_to):
|
||||||
|
doc = frappe.new_doc("Payment Entry")
|
||||||
|
doc.company = self.company
|
||||||
|
doc.payment_type = "Pay"
|
||||||
|
doc.mode_of_payment = mode_of_payment
|
||||||
|
doc.party_type = "Employee"
|
||||||
|
doc.party = self.employee
|
||||||
|
doc.paid_from = paid_from
|
||||||
|
doc.paid_to = paid_to
|
||||||
|
doc.paid_amount = self.total_commission_amount
|
||||||
|
doc.received_amount = self.total_commission_amount
|
||||||
|
doc.source_exchange_rate = 1
|
||||||
|
doc.target_exchange_rate = 1
|
||||||
|
doc.set("references", [])
|
||||||
|
self.add_references(doc)
|
||||||
|
doc.submit()
|
||||||
|
|
||||||
|
self.db_set("reference_doctype", "Payment Entry")
|
||||||
|
self.db_set("reference_name", doc.name)
|
||||||
|
|
||||||
|
def add_references(self, doc):
|
||||||
|
reference = {}
|
||||||
|
reference['reference_doctype'] = "Sales Commission"
|
||||||
|
reference['reference_name'] = self.name
|
||||||
|
reference['due_date'] = self.to_date
|
||||||
|
reference['total_amount'] = self.total_commission_amount
|
||||||
|
reference['outstanding_amount'] = self.total_commission_amount
|
||||||
|
reference['allocated_amount'] = self.total_commission_amount
|
||||||
|
doc.append("references", reference)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# See license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TestSalesCommission(unittest.TestCase):
|
||||||
|
pass
|
||||||
@@ -32,7 +32,8 @@
|
|||||||
"column_break_5",
|
"column_break_5",
|
||||||
"allow_multiple_items",
|
"allow_multiple_items",
|
||||||
"allow_against_multiple_purchase_orders",
|
"allow_against_multiple_purchase_orders",
|
||||||
"hide_tax_id"
|
"hide_tax_id",
|
||||||
|
"approval_required_for_sales_commission_payout"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -197,6 +198,12 @@
|
|||||||
"fieldname": "editable_bundle_item_rates",
|
"fieldname": "editable_bundle_item_rates",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Calculate Product Bundle Price based on Child Items' Rates"
|
"label": "Calculate Product Bundle Price based on Child Items' Rates"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "approval_required_for_sales_commission_payout",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Approval Required for Sales Commission Payout"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-cog",
|
"icon": "fa fa-cog",
|
||||||
@@ -204,7 +211,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-09-08 19:38:10.175989",
|
"modified": "2021-09-20 15:43:29.319488",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Selling Settings",
|
"name": "Selling Settings",
|
||||||
|
|||||||
Reference in New Issue
Block a user