Merge branch 'develop' into loan_manager_amend
This commit is contained in:
commit
6d88cf9ede
7
.github/workflows/ci-tests.yml
vendored
7
.github/workflows/ci-tests.yml
vendored
@ -85,10 +85,9 @@ jobs:
|
||||
run: |
|
||||
cp ~/frappe-bench/sites/.coverage ${GITHUB_WORKSPACE}
|
||||
cd ${GITHUB_WORKSPACE}
|
||||
pip install coveralls==2.2.0
|
||||
pip install coverage==4.5.4
|
||||
coveralls
|
||||
pip install coveralls==3.0.1
|
||||
pip install coverage==5.5
|
||||
coveralls --service=github
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_TOKEN }}
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
"frozen_accounts_modifier",
|
||||
"determine_address_tax_category_from",
|
||||
"over_billing_allowance",
|
||||
"role_allowed_to_over_bill",
|
||||
"column_break_4",
|
||||
"credit_controller",
|
||||
"check_supplier_invoice_uniqueness",
|
||||
@ -226,6 +227,13 @@
|
||||
"fieldname": "delete_linked_ledger_entries",
|
||||
"fieldtype": "Check",
|
||||
"label": "Delete Accounting and Stock Ledger Entries on deletion of Transaction"
|
||||
},
|
||||
{
|
||||
"description": "Users with this role are allowed to over bill above the allowance percentage",
|
||||
"fieldname": "role_allowed_to_over_bill",
|
||||
"fieldtype": "Link",
|
||||
"label": "Role Allowed to Over Bill ",
|
||||
"options": "Role"
|
||||
}
|
||||
],
|
||||
"icon": "icon-cog",
|
||||
@ -233,7 +241,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2021-01-05 13:04:00.118892",
|
||||
"modified": "2021-03-11 18:52:05.601996",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Accounts Settings",
|
||||
|
@ -89,8 +89,8 @@ class POSClosingEntry(StatusUpdater):
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_cashiers(doctype, txt, searchfield, start, page_len, filters):
|
||||
cashiers_list = frappe.get_all("POS Profile User", filters=filters, fields=['user'])
|
||||
return [c['user'] for c in cashiers_list]
|
||||
cashiers_list = frappe.get_all("POS Profile User", filters=filters, fields=['user'], as_list=1)
|
||||
return [c for c in cashiers_list]
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_pos_invoices(start, end, pos_profile, user):
|
||||
|
@ -173,7 +173,7 @@ def _get_tree_conditions(args, parenttype, table, allow_blank=True):
|
||||
if parenttype in ["Customer Group", "Item Group", "Territory"]:
|
||||
parent_field = "parent_{0}".format(frappe.scrub(parenttype))
|
||||
root_name = frappe.db.get_list(parenttype,
|
||||
{"is_group": 1, parent_field: ("is", "not set")}, "name", as_list=1)
|
||||
{"is_group": 1, parent_field: ("is", "not set")}, "name", as_list=1, ignore_permissions=True)
|
||||
|
||||
if root_name and root_name[0][0]:
|
||||
parent_groups.append(root_name[0][0])
|
||||
|
@ -717,7 +717,9 @@ class AccountsController(TransactionBase):
|
||||
total_billed_amt = abs(total_billed_amt)
|
||||
max_allowed_amt = abs(max_allowed_amt)
|
||||
|
||||
if total_billed_amt - max_allowed_amt > 0.01:
|
||||
role_allowed_to_over_bill = frappe.db.get_single_value('Accounts Settings', 'role_allowed_to_over_bill')
|
||||
|
||||
if total_billed_amt - max_allowed_amt > 0.01 and role_allowed_to_over_bill not in frappe.get_roles():
|
||||
frappe.throw(_("Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings")
|
||||
.format(item.item_code, item.idx, max_allowed_amt))
|
||||
|
||||
|
@ -201,10 +201,14 @@ class StatusUpdater(Document):
|
||||
get_allowance_for(item['item_code'], self.item_allowance,
|
||||
self.global_qty_allowance, self.global_amount_allowance, qty_or_amount)
|
||||
|
||||
overflow_percent = ((item[args['target_field']] - item[args['target_ref_field']]) /
|
||||
item[args['target_ref_field']]) * 100
|
||||
role_allowed_to_over_deliver_receive = frappe.db.get_single_value('Stock Settings', 'role_allowed_to_over_deliver_receive')
|
||||
role_allowed_to_over_bill = frappe.db.get_single_value('Accounts Settings', 'role_allowed_to_over_bill')
|
||||
role = role_allowed_to_over_deliver_receive if qty_or_amount == 'qty' else role_allowed_to_over_bill
|
||||
|
||||
if overflow_percent - allowance > 0.01:
|
||||
overflow_percent = ((item[args['target_field']] - item[args['target_ref_field']]) /
|
||||
item[args['target_ref_field']]) * 100
|
||||
|
||||
if overflow_percent - allowance > 0.01 and role not in frappe.get_roles():
|
||||
item['max_allowed'] = flt(item[args['target_ref_field']] * (100+allowance)/100)
|
||||
item['reduce_by'] = item[args['target_field']] - item['max_allowed']
|
||||
|
||||
|
@ -1,206 +1,64 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2018-07-12 12:07:36.932333",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"actions": [],
|
||||
"creation": "2018-07-12 12:07:36.932333",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"service_unit",
|
||||
"check_in",
|
||||
"left",
|
||||
"check_out",
|
||||
"invoiced"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "service_unit",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Healthcare Service Unit",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Healthcare Service Unit",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "service_unit",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Healthcare Service Unit",
|
||||
"options": "Healthcare Service Unit",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "check_in",
|
||||
"fieldtype": "Datetime",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Check In",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "check_in",
|
||||
"fieldtype": "Datetime",
|
||||
"in_list_view": 1,
|
||||
"label": "Check In"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "left",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Left",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 1,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"default": "0",
|
||||
"fieldname": "left",
|
||||
"fieldtype": "Check",
|
||||
"label": "Left",
|
||||
"read_only": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "check_out",
|
||||
"fieldtype": "Datetime",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Check Out",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "check_out",
|
||||
"fieldtype": "Datetime",
|
||||
"label": "Check Out"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "0",
|
||||
"fieldname": "invoiced",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Invoiced",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"default": "0",
|
||||
"fieldname": "invoiced",
|
||||
"fieldtype": "Check",
|
||||
"label": "Invoiced",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-11-04 03:33:26.958713",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Healthcare",
|
||||
"name": "Inpatient Occupancy",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"restrict_to_domain": "Healthcare",
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-03-18 15:08:54.634132",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Healthcare",
|
||||
"name": "Inpatient Occupancy",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"restrict_to_domain": "Healthcare",
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
@ -185,7 +185,7 @@
|
||||
"fieldtype": "Datetime",
|
||||
"in_list_view": 1,
|
||||
"label": "Admitted Datetime",
|
||||
"read_only": 1
|
||||
"permlevel": 2
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:(doc.expected_length_of_stay > 0)",
|
||||
@ -312,7 +312,7 @@
|
||||
"fieldname": "inpatient_occupancies",
|
||||
"fieldtype": "Table",
|
||||
"options": "Inpatient Occupancy",
|
||||
"read_only": 1
|
||||
"permlevel": 2
|
||||
},
|
||||
{
|
||||
"fieldname": "btn_transfer",
|
||||
@ -407,12 +407,12 @@
|
||||
"fieldname": "discharge_datetime",
|
||||
"fieldtype": "Datetime",
|
||||
"label": "Discharge Date",
|
||||
"read_only": 1
|
||||
"permlevel": 2
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2021-03-18 14:44:11.689956",
|
||||
"modified": "2021-03-18 15:59:17.318988",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Healthcare",
|
||||
"name": "Inpatient Record",
|
||||
@ -465,6 +465,37 @@
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Nursing User"
|
||||
},
|
||||
{
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"permlevel": 2,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Healthcare Administrator",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"permlevel": 2,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Physician",
|
||||
"share": 1
|
||||
},
|
||||
{
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"permlevel": 2,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Nursing User",
|
||||
"share": 1
|
||||
}
|
||||
],
|
||||
"restrict_to_domain": "Healthcare",
|
||||
|
@ -66,7 +66,7 @@ class CompensatoryLeaveRequest(Document):
|
||||
|
||||
else:
|
||||
leave_allocation = self.create_leave_allocation(leave_period, date_difference)
|
||||
self.leave_allocation=leave_allocation.name
|
||||
self.db_set("leave_allocation", leave_allocation.name)
|
||||
else:
|
||||
frappe.throw(_("There is no leave period in between {0} and {1}").format(format_date(self.work_from_date), format_date(self.work_end_date)))
|
||||
|
||||
@ -124,4 +124,4 @@ class CompensatoryLeaveRequest(Document):
|
||||
))
|
||||
allocation.insert(ignore_permissions=True)
|
||||
allocation.submit()
|
||||
return allocation
|
||||
return allocation
|
||||
|
@ -151,6 +151,10 @@ frappe.ui.form.on('Payroll Entry', {
|
||||
filters['company'] = frm.doc.company;
|
||||
filters['start_date'] = frm.doc.start_date;
|
||||
filters['end_date'] = frm.doc.end_date;
|
||||
filters['salary_slip_based_on_timesheet'] = frm.doc.salary_slip_based_on_timesheet;
|
||||
filters['payroll_frequency'] = frm.doc.payroll_frequency;
|
||||
filters['payroll_payable_account'] = frm.doc.payroll_payable_account;
|
||||
filters['currency'] = frm.doc.currency;
|
||||
|
||||
if (frm.doc.department) {
|
||||
filters['department'] = frm.doc.department;
|
||||
|
@ -52,49 +52,32 @@ class PayrollEntry(Document):
|
||||
Returns list of active employees based on selected criteria
|
||||
and for which salary structure exists
|
||||
"""
|
||||
cond = self.get_filter_condition()
|
||||
cond += self.get_joining_relieving_condition()
|
||||
self.check_mandatory()
|
||||
filters = self.make_filters()
|
||||
cond = get_filter_condition(filters)
|
||||
cond += get_joining_relieving_condition(self.start_date, self.end_date)
|
||||
|
||||
condition = ''
|
||||
if self.payroll_frequency:
|
||||
condition = """and payroll_frequency = '%(payroll_frequency)s'"""% {"payroll_frequency": self.payroll_frequency}
|
||||
|
||||
sal_struct = frappe.db.sql_list("""
|
||||
select
|
||||
name from `tabSalary Structure`
|
||||
where
|
||||
docstatus = 1 and
|
||||
is_active = 'Yes'
|
||||
and company = %(company)s
|
||||
and currency = %(currency)s and
|
||||
ifnull(salary_slip_based_on_timesheet,0) = %(salary_slip_based_on_timesheet)s
|
||||
{condition}""".format(condition=condition),
|
||||
{"company": self.company, "currency": self.currency, "salary_slip_based_on_timesheet":self.salary_slip_based_on_timesheet})
|
||||
|
||||
sal_struct = get_sal_struct(self.company, self.currency, self.salary_slip_based_on_timesheet, condition)
|
||||
if sal_struct:
|
||||
cond += "and t2.salary_structure IN %(sal_struct)s "
|
||||
cond += "and t2.payroll_payable_account = %(payroll_payable_account)s "
|
||||
cond += "and %(from_date)s >= t2.from_date"
|
||||
emp_list = frappe.db.sql("""
|
||||
select
|
||||
distinct t1.name as employee, t1.employee_name, t1.department, t1.designation
|
||||
from
|
||||
`tabEmployee` t1, `tabSalary Structure Assignment` t2
|
||||
where
|
||||
t1.name = t2.employee
|
||||
and t2.docstatus = 1
|
||||
%s order by t2.from_date desc
|
||||
""" % cond, {"sal_struct": tuple(sal_struct), "from_date": self.end_date, "payroll_payable_account": self.payroll_payable_account}, as_dict=True)
|
||||
|
||||
emp_list = self.remove_payrolled_employees(emp_list)
|
||||
emp_list = get_emp_list(sal_struct, cond, self.end_date, self.payroll_payable_account)
|
||||
emp_list = remove_payrolled_employees(emp_list, self.start_date, self.end_date)
|
||||
return emp_list
|
||||
|
||||
def remove_payrolled_employees(self, emp_list):
|
||||
for employee_details in emp_list:
|
||||
if frappe.db.exists("Salary Slip", {"employee": employee_details.employee, "start_date": self.start_date, "end_date": self.end_date, "docstatus": 1}):
|
||||
emp_list.remove(employee_details)
|
||||
def make_filters(self):
|
||||
filters = frappe._dict()
|
||||
filters['company'] = self.company
|
||||
filters['branch'] = self.branch
|
||||
filters['department'] = self.department
|
||||
filters['designation'] = self.designation
|
||||
|
||||
return emp_list
|
||||
return filters
|
||||
|
||||
@frappe.whitelist()
|
||||
def fill_employee_details(self):
|
||||
@ -122,23 +105,6 @@ class PayrollEntry(Document):
|
||||
if self.validate_attendance:
|
||||
return self.validate_employee_attendance()
|
||||
|
||||
def get_filter_condition(self):
|
||||
self.check_mandatory()
|
||||
|
||||
cond = ''
|
||||
for f in ['company', 'branch', 'department', 'designation']:
|
||||
if self.get(f):
|
||||
cond += " and t1." + f + " = " + frappe.db.escape(self.get(f))
|
||||
|
||||
return cond
|
||||
|
||||
def get_joining_relieving_condition(self):
|
||||
cond = """
|
||||
and ifnull(t1.date_of_joining, '0000-00-00') <= '%(end_date)s'
|
||||
and ifnull(t1.relieving_date, '2199-12-31') >= '%(start_date)s'
|
||||
""" % {"start_date": self.start_date, "end_date": self.end_date}
|
||||
return cond
|
||||
|
||||
def check_mandatory(self):
|
||||
for fieldname in ['company', 'start_date', 'end_date']:
|
||||
if not self.get(fieldname):
|
||||
@ -451,6 +417,53 @@ class PayrollEntry(Document):
|
||||
marked_days = attendances[0][0]
|
||||
return marked_days
|
||||
|
||||
def get_sal_struct(company, currency, salary_slip_based_on_timesheet, condition):
|
||||
return frappe.db.sql_list("""
|
||||
select
|
||||
name from `tabSalary Structure`
|
||||
where
|
||||
docstatus = 1 and
|
||||
is_active = 'Yes'
|
||||
and company = %(company)s
|
||||
and currency = %(currency)s and
|
||||
ifnull(salary_slip_based_on_timesheet,0) = %(salary_slip_based_on_timesheet)s
|
||||
{condition}""".format(condition=condition),
|
||||
{"company": company, "currency": currency, "salary_slip_based_on_timesheet": salary_slip_based_on_timesheet})
|
||||
|
||||
def get_filter_condition(filters):
|
||||
cond = ''
|
||||
for f in ['company', 'branch', 'department', 'designation']:
|
||||
if filters.get(f):
|
||||
cond += " and t1." + f + " = " + frappe.db.escape(filters.get(f))
|
||||
|
||||
return cond
|
||||
|
||||
def get_joining_relieving_condition(start_date, end_date):
|
||||
cond = """
|
||||
and ifnull(t1.date_of_joining, '0000-00-00') <= '%(end_date)s'
|
||||
and ifnull(t1.relieving_date, '2199-12-31') >= '%(start_date)s'
|
||||
""" % {"start_date": start_date, "end_date": end_date}
|
||||
return cond
|
||||
|
||||
def get_emp_list(sal_struct, cond, end_date, payroll_payable_account):
|
||||
return frappe.db.sql("""
|
||||
select
|
||||
distinct t1.name as employee, t1.employee_name, t1.department, t1.designation
|
||||
from
|
||||
`tabEmployee` t1, `tabSalary Structure Assignment` t2
|
||||
where
|
||||
t1.name = t2.employee
|
||||
and t2.docstatus = 1
|
||||
%s order by t2.from_date desc
|
||||
""" % cond, {"sal_struct": tuple(sal_struct), "from_date": end_date, "payroll_payable_account": payroll_payable_account}, as_dict=True)
|
||||
|
||||
def remove_payrolled_employees(emp_list, start_date, end_date):
|
||||
for employee_details in emp_list:
|
||||
if frappe.db.exists("Salary Slip", {"employee": employee_details.employee, "start_date": start_date, "end_date": end_date, "docstatus": 1}):
|
||||
emp_list.remove(employee_details)
|
||||
|
||||
return emp_list
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_start_end_dates(payroll_frequency, start_date=None, company=None):
|
||||
'''Returns dict of start and end dates for given payroll frequency based on start_date'''
|
||||
@ -639,39 +652,41 @@ def get_payroll_entries_for_jv(doctype, txt, searchfield, start, page_len, filte
|
||||
'start': start, 'page_len': page_len
|
||||
})
|
||||
|
||||
def get_employee_with_existing_salary_slip(start_date, end_date, company):
|
||||
return frappe.db.sql_list("""
|
||||
select employee from `tabSalary Slip`
|
||||
where
|
||||
(start_date between %(start_date)s and %(end_date)s
|
||||
or
|
||||
end_date between %(start_date)s and %(end_date)s
|
||||
or
|
||||
%(start_date)s between start_date and end_date)
|
||||
and company = %(company)s
|
||||
and docstatus = 1
|
||||
""", {'start_date': start_date, 'end_date': end_date, 'company': company})
|
||||
def get_employee_list(filters):
|
||||
cond = get_filter_condition(filters)
|
||||
cond += get_joining_relieving_condition(filters.start_date, filters.end_date)
|
||||
condition = """and payroll_frequency = '%(payroll_frequency)s'"""% {"payroll_frequency": filters.payroll_frequency}
|
||||
sal_struct = get_sal_struct(filters.company, filters.currency, filters.salary_slip_based_on_timesheet, condition)
|
||||
if sal_struct:
|
||||
cond += "and t2.salary_structure IN %(sal_struct)s "
|
||||
cond += "and t2.payroll_payable_account = %(payroll_payable_account)s "
|
||||
cond += "and %(from_date)s >= t2.from_date"
|
||||
emp_list = get_emp_list(sal_struct, cond, filters.end_date, filters.payroll_payable_account)
|
||||
emp_list = remove_payrolled_employees(emp_list, filters.start_date, filters.end_date)
|
||||
return emp_list
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def employee_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
filters = frappe._dict(filters)
|
||||
conditions = []
|
||||
exclude_employees = []
|
||||
include_employees = []
|
||||
emp_cond = ''
|
||||
if filters.start_date and filters.end_date:
|
||||
employee_list = get_employee_with_existing_salary_slip(filters.start_date, filters.end_date, filters.company)
|
||||
employee_list = get_employee_list(filters)
|
||||
emp = filters.get('employees')
|
||||
include_employees = [employee.employee for employee in employee_list if employee.employee not in emp]
|
||||
filters.pop('start_date')
|
||||
filters.pop('end_date')
|
||||
filters.pop('salary_slip_based_on_timesheet')
|
||||
filters.pop('payroll_frequency')
|
||||
filters.pop('payroll_payable_account')
|
||||
filters.pop('currency')
|
||||
if filters.employees is not None:
|
||||
filters.pop('employees')
|
||||
if employee_list:
|
||||
exclude_employees.extend(employee_list)
|
||||
if emp:
|
||||
exclude_employees.extend(emp)
|
||||
if exclude_employees:
|
||||
emp_cond += 'and employee not in %(exclude_employees)s'
|
||||
|
||||
if include_employees:
|
||||
emp_cond += 'and employee in %(include_employees)s'
|
||||
|
||||
return frappe.db.sql("""select name, employee_name from `tabEmployee`
|
||||
where status = 'Active'
|
||||
@ -695,4 +710,4 @@ def employee_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
'_txt': txt.replace("%", ""),
|
||||
'start': start,
|
||||
'page_len': page_len,
|
||||
'exclude_employees': exclude_employees})
|
||||
'include_employees': include_employees})
|
||||
|
@ -133,8 +133,6 @@ frappe.ui.form.on('Salary Structure', {
|
||||
title: __("Assign to Employees"),
|
||||
fields: [
|
||||
{fieldname: "sec_break", fieldtype: "Section Break", label: __("Filter Employees By (Optional)")},
|
||||
{fieldname: "company", fieldtype: "Link", options: "Company", label: __("Company"), default: frm.doc.company, read_only:1},
|
||||
{fieldname: "currency", fieldtype: "Link", options: "Currency", label: __("Currency"), default: frm.doc.currency, read_only:1},
|
||||
{fieldname: "grade", fieldtype: "Link", options: "Employee Grade", label: __("Employee Grade")},
|
||||
{fieldname:'department', fieldtype:'Link', options: 'Department', label: __('Department')},
|
||||
{fieldname:'designation', fieldtype:'Link', options: 'Designation', label: __('Designation')},
|
||||
|
@ -88,7 +88,7 @@ class SalaryStructure(Document):
|
||||
return employees
|
||||
|
||||
@frappe.whitelist()
|
||||
def assign_salary_structure(self, grade=None, department=None, designation=None,employee=None,
|
||||
def assign_salary_structure(self, grade=None, department=None, designation=None, employee=None,
|
||||
payroll_payable_account=None, from_date=None, base=None, variable=None, income_tax_slab=None):
|
||||
employees = self.get_employees(company= self.company, grade= grade,department= department,designation= designation,name=employee)
|
||||
|
||||
|
@ -11,15 +11,16 @@
|
||||
"project",
|
||||
"issue",
|
||||
"type",
|
||||
"color",
|
||||
"is_group",
|
||||
"is_template",
|
||||
"column_break0",
|
||||
"status",
|
||||
"priority",
|
||||
"task_weight",
|
||||
"completed_by",
|
||||
"color",
|
||||
"parent_task",
|
||||
"completed_by",
|
||||
"completed_on",
|
||||
"sb_timeline",
|
||||
"exp_start_date",
|
||||
"expected_time",
|
||||
@ -358,6 +359,7 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.status == \"Completed\"",
|
||||
"fieldname": "completed_by",
|
||||
"fieldtype": "Link",
|
||||
"label": "Completed By",
|
||||
@ -381,6 +383,13 @@
|
||||
"fieldname": "duration",
|
||||
"fieldtype": "Int",
|
||||
"label": "Duration (Days)"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.status == \"Completed\"",
|
||||
"fieldname": "completed_on",
|
||||
"fieldtype": "Date",
|
||||
"label": "Completed On",
|
||||
"mandatory_depends_on": "eval: doc.status == \"Completed\""
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-check",
|
||||
@ -388,7 +397,7 @@
|
||||
"is_tree": 1,
|
||||
"links": [],
|
||||
"max_attachments": 5,
|
||||
"modified": "2020-12-28 11:32:58.714991",
|
||||
"modified": "2021-04-16 12:46:51.556741",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Projects",
|
||||
"name": "Task",
|
||||
|
@ -36,6 +36,7 @@ class Task(NestedSet):
|
||||
self.validate_status()
|
||||
self.update_depends_on()
|
||||
self.validate_dependencies_for_template_task()
|
||||
self.validate_completed_on()
|
||||
|
||||
def validate_dates(self):
|
||||
if self.exp_start_date and self.exp_end_date and getdate(self.exp_start_date) > getdate(self.exp_end_date):
|
||||
@ -100,6 +101,10 @@ class Task(NestedSet):
|
||||
dependent_task_format = """<a href="#Form/Task/{0}">{0}</a>""".format(task.task)
|
||||
frappe.throw(_("Dependent Task {0} is not a Template Task").format(dependent_task_format))
|
||||
|
||||
def validate_completed_on(self):
|
||||
if self.completed_on and getdate(self.completed_on) > getdate():
|
||||
frappe.throw(_("Completed On cannot be greater than Today"))
|
||||
|
||||
def update_depends_on(self):
|
||||
depends_on_tasks = self.depends_on_tasks or ""
|
||||
for d in self.depends_on:
|
||||
|
@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
frappe.query_reports["Delayed Tasks Summary"] = {
|
||||
"filters": [
|
||||
{
|
||||
"fieldname": "from_date",
|
||||
"label": __("From Date"),
|
||||
"fieldtype": "Date"
|
||||
},
|
||||
{
|
||||
"fieldname": "to_date",
|
||||
"label": __("To Date"),
|
||||
"fieldtype": "Date"
|
||||
},
|
||||
{
|
||||
"fieldname": "priority",
|
||||
"label": __("Priority"),
|
||||
"fieldtype": "Select",
|
||||
"options": ["", "Low", "Medium", "High", "Urgent"]
|
||||
},
|
||||
{
|
||||
"fieldname": "status",
|
||||
"label": __("Status"),
|
||||
"fieldtype": "Select",
|
||||
"options": ["", "Open", "Working","Pending Review","Overdue","Completed"]
|
||||
},
|
||||
],
|
||||
"formatter": function(value, row, column, data, default_formatter) {
|
||||
value = default_formatter(value, row, column, data);
|
||||
if (column.id == "delay") {
|
||||
if (data["delay"] > 0) {
|
||||
value = `<p style="color: red; font-weight: bold">${value}</p>`;
|
||||
} else {
|
||||
value = `<p style="color: green; font-weight: bold">${value}</p>`;
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
};
|
@ -0,0 +1,29 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"columns": [],
|
||||
"creation": "2021-03-25 15:03:19.857418",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"filters": [],
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2021-04-15 15:49:35.432486",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Projects",
|
||||
"name": "Delayed Tasks Summary",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Task",
|
||||
"report_name": "Delayed Tasks Summary",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Projects User"
|
||||
},
|
||||
{
|
||||
"role": "Projects Manager"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.utils import date_diff, nowdate
|
||||
|
||||
def execute(filters=None):
|
||||
columns, data = [], []
|
||||
data = get_data(filters)
|
||||
columns = get_columns()
|
||||
charts = get_chart_data(data)
|
||||
return columns, data, None, charts
|
||||
|
||||
def get_data(filters):
|
||||
conditions = get_conditions(filters)
|
||||
tasks = frappe.get_all("Task",
|
||||
filters = conditions,
|
||||
fields = ["name", "subject", "exp_start_date", "exp_end_date",
|
||||
"status", "priority", "completed_on", "progress"],
|
||||
order_by="creation"
|
||||
)
|
||||
for task in tasks:
|
||||
if task.exp_end_date:
|
||||
if task.completed_on:
|
||||
task.delay = date_diff(task.completed_on, task.exp_end_date)
|
||||
elif task.status == "Completed":
|
||||
# task is completed but completed on is not set (for older tasks)
|
||||
task.delay = 0
|
||||
else:
|
||||
# task not completed
|
||||
task.delay = date_diff(nowdate(), task.exp_end_date)
|
||||
else:
|
||||
# task has no end date, hence no delay
|
||||
task.delay = 0
|
||||
|
||||
# Sort by descending order of delay
|
||||
tasks.sort(key=lambda x: x["delay"], reverse=True)
|
||||
return tasks
|
||||
|
||||
def get_conditions(filters):
|
||||
conditions = frappe._dict()
|
||||
keys = ["priority", "status"]
|
||||
for key in keys:
|
||||
if filters.get(key):
|
||||
conditions[key] = filters.get(key)
|
||||
if filters.get("from_date"):
|
||||
conditions.exp_end_date = [">=", filters.get("from_date")]
|
||||
if filters.get("to_date"):
|
||||
conditions.exp_start_date = ["<=", filters.get("to_date")]
|
||||
return conditions
|
||||
|
||||
def get_chart_data(data):
|
||||
delay, on_track = 0, 0
|
||||
for entry in data:
|
||||
if entry.get("delay") > 0:
|
||||
delay = delay + 1
|
||||
else:
|
||||
on_track = on_track + 1
|
||||
charts = {
|
||||
"data": {
|
||||
"labels": ["On Track", "Delayed"],
|
||||
"datasets": [
|
||||
{
|
||||
"name": "Delayed",
|
||||
"values": [on_track, delay]
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "percentage",
|
||||
"colors": ["#84D5BA", "#CB4B5F"]
|
||||
}
|
||||
return charts
|
||||
|
||||
def get_columns():
|
||||
columns = [
|
||||
{
|
||||
"fieldname": "name",
|
||||
"fieldtype": "Link",
|
||||
"label": "Task",
|
||||
"options": "Task",
|
||||
"width": 150
|
||||
},
|
||||
{
|
||||
"fieldname": "subject",
|
||||
"fieldtype": "Data",
|
||||
"label": "Subject",
|
||||
"width": 200
|
||||
},
|
||||
{
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Data",
|
||||
"label": "Status",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"fieldname": "priority",
|
||||
"fieldtype": "Data",
|
||||
"label": "Priority",
|
||||
"width": 80
|
||||
},
|
||||
{
|
||||
"fieldname": "progress",
|
||||
"fieldtype": "Data",
|
||||
"label": "Progress (%)",
|
||||
"width": 120
|
||||
},
|
||||
{
|
||||
"fieldname": "exp_start_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Expected Start Date",
|
||||
"width": 150
|
||||
},
|
||||
{
|
||||
"fieldname": "exp_end_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Expected End Date",
|
||||
"width": 150
|
||||
},
|
||||
{
|
||||
"fieldname": "completed_on",
|
||||
"fieldtype": "Date",
|
||||
"label": "Actual End Date",
|
||||
"width": 130
|
||||
},
|
||||
{
|
||||
"fieldname": "delay",
|
||||
"fieldtype": "Data",
|
||||
"label": "Delay (In Days)",
|
||||
"width": 120
|
||||
}
|
||||
]
|
||||
return columns
|
@ -0,0 +1,54 @@
|
||||
from __future__ import unicode_literals
|
||||
import unittest
|
||||
import frappe
|
||||
from frappe.utils import nowdate, add_days, add_months
|
||||
from erpnext.projects.doctype.task.test_task import create_task
|
||||
from erpnext.projects.report.delayed_tasks_summary.delayed_tasks_summary import execute
|
||||
|
||||
class TestDelayedTasksSummary(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUp(self):
|
||||
task1 = create_task("_Test Task 98", add_days(nowdate(), -10), nowdate())
|
||||
create_task("_Test Task 99", add_days(nowdate(), -10), add_days(nowdate(), -1))
|
||||
|
||||
task1.status = "Completed"
|
||||
task1.completed_on = add_days(nowdate(), -1)
|
||||
task1.save()
|
||||
|
||||
def test_delayed_tasks_summary(self):
|
||||
filters = frappe._dict({
|
||||
"from_date": add_months(nowdate(), -1),
|
||||
"to_date": nowdate(),
|
||||
"priority": "Low",
|
||||
"status": "Open"
|
||||
})
|
||||
expected_data = [
|
||||
{
|
||||
"subject": "_Test Task 99",
|
||||
"status": "Open",
|
||||
"priority": "Low",
|
||||
"delay": 1
|
||||
},
|
||||
{
|
||||
"subject": "_Test Task 98",
|
||||
"status": "Completed",
|
||||
"priority": "Low",
|
||||
"delay": -1
|
||||
}
|
||||
]
|
||||
report = execute(filters)
|
||||
data = list(filter(lambda x: x.subject == "_Test Task 99", report[1]))[0]
|
||||
|
||||
for key in ["subject", "status", "priority", "delay"]:
|
||||
self.assertEqual(expected_data[0].get(key), data.get(key))
|
||||
|
||||
filters.status = "Completed"
|
||||
report = execute(filters)
|
||||
data = list(filter(lambda x: x.subject == "_Test Task 98", report[1]))[0]
|
||||
|
||||
for key in ["subject", "status", "priority", "delay"]:
|
||||
self.assertEqual(expected_data[1].get(key), data.get(key))
|
||||
|
||||
def tearDown(self):
|
||||
for task in ["_Test Task 98", "_Test Task 99"]:
|
||||
frappe.get_doc("Task", {"subject": task}).delete()
|
@ -15,6 +15,7 @@
|
||||
"hide_custom": 0,
|
||||
"icon": "project",
|
||||
"idx": 0,
|
||||
"is_default": 0,
|
||||
"is_standard": 1,
|
||||
"label": "Projects",
|
||||
"links": [
|
||||
@ -148,9 +149,19 @@
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Task",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Delayed Tasks Summary",
|
||||
"link_to": "Delayed Tasks Summary",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2020-12-01 13:38:37.856224",
|
||||
"modified": "2021-03-26 16:32:00.628561",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Projects",
|
||||
"name": "Projects",
|
||||
|
@ -1103,6 +1103,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
to_currency: to_currency,
|
||||
args: args
|
||||
},
|
||||
freeze: true,
|
||||
freeze_message: __("Fetching exchange rates ..."),
|
||||
callback: function(r) {
|
||||
callback(flt(r.message));
|
||||
}
|
||||
|
@ -159,6 +159,31 @@ erpnext.PointOfSale.ItemSelector = class {
|
||||
bind_events() {
|
||||
const me = this;
|
||||
window.onScan = onScan;
|
||||
|
||||
onScan.decodeKeyEvent = function (oEvent) {
|
||||
var iCode = this._getNormalizedKeyNum(oEvent);
|
||||
switch (true) {
|
||||
case iCode >= 48 && iCode <= 90: // numbers and letters
|
||||
case iCode >= 106 && iCode <= 111: // operations on numeric keypad (+, -, etc.)
|
||||
case (iCode >= 160 && iCode <= 164) || iCode == 170: // ^ ! # $ *
|
||||
case iCode >= 186 && iCode <= 194: // (; = , - . / `)
|
||||
case iCode >= 219 && iCode <= 222: // ([ \ ] ')
|
||||
if (oEvent.key !== undefined && oEvent.key !== '') {
|
||||
return oEvent.key;
|
||||
}
|
||||
|
||||
var sDecoded = String.fromCharCode(iCode);
|
||||
switch (oEvent.shiftKey) {
|
||||
case false: sDecoded = sDecoded.toLowerCase(); break;
|
||||
case true: sDecoded = sDecoded.toUpperCase(); break;
|
||||
}
|
||||
return sDecoded;
|
||||
case iCode >= 96 && iCode <= 105: // numbers on numeric keypad
|
||||
return 0 + (iCode - 96);
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
onScan.attachTo(document, {
|
||||
onScan: (sScancode) => {
|
||||
if (this.search_field && this.$component.is(':visible')) {
|
||||
|
@ -248,177 +248,9 @@
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Healthcare",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Patient",
|
||||
"link_to": "Patient",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Diagnosis",
|
||||
"link_to": "Diagnosis",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Education",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Student",
|
||||
"link_to": "Student",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Instructor",
|
||||
"link_to": "Instructor",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Course",
|
||||
"link_to": "Course",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Room",
|
||||
"link_to": "Room",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Non Profit",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Donor",
|
||||
"link_to": "Donor",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Member",
|
||||
"link_to": "Member",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Volunteer",
|
||||
"link_to": "Volunteer",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Chapter",
|
||||
"link_to": "Chapter",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Agriculture",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Location",
|
||||
"link_to": "Location",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Crop",
|
||||
"link_to": "Crop",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Crop Cycle",
|
||||
"link_to": "Crop Cycle",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Fertilizer",
|
||||
"link_to": "Fertilizer",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2021-03-16 15:59:58.416154",
|
||||
"modified": "2021-04-19 15:48:44.089927",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Home",
|
||||
|
@ -13,6 +13,7 @@
|
||||
"column_break_4",
|
||||
"valuation_method",
|
||||
"over_delivery_receipt_allowance",
|
||||
"role_allowed_to_over_deliver_receive",
|
||||
"action_if_quality_inspection_is_not_submitted",
|
||||
"show_barcode_field",
|
||||
"clean_description_html",
|
||||
@ -234,6 +235,13 @@
|
||||
"fieldname": "disable_serial_no_and_batch_selector",
|
||||
"fieldtype": "Check",
|
||||
"label": "Disable Serial No And Batch Selector"
|
||||
},
|
||||
{
|
||||
"description": "Users with this role are allowed to over deliver/receive against orders above the allowance percentage",
|
||||
"fieldname": "role_allowed_to_over_deliver_receive",
|
||||
"fieldtype": "Link",
|
||||
"label": "Role Allowed to Over Deliver/Receive",
|
||||
"options": "Role"
|
||||
}
|
||||
],
|
||||
"icon": "icon-cog",
|
||||
@ -241,7 +249,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2021-01-18 13:15:38.352796",
|
||||
"modified": "2021-03-11 18:48:14.513055",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Settings",
|
||||
|
@ -609,8 +609,12 @@ def get_price_list_rate(args, item_doc, out):
|
||||
meta = frappe.get_meta(args.parenttype or args.doctype)
|
||||
|
||||
if meta.get_field("currency") or args.get('currency'):
|
||||
pl_details = get_price_list_currency_and_exchange_rate(args)
|
||||
args.update(pl_details)
|
||||
if not args.get("price_list_currency") or not args.get("plc_conversion_rate"):
|
||||
# if currency and plc_conversion_rate exist then
|
||||
# `get_price_list_currency_and_exchange_rate` has already been called
|
||||
pl_details = get_price_list_currency_and_exchange_rate(args)
|
||||
args.update(pl_details)
|
||||
|
||||
if meta.get_field("currency"):
|
||||
validate_conversion_rate(args, meta)
|
||||
|
||||
@ -1000,6 +1004,8 @@ def apply_price_list(args, as_doc=False):
|
||||
args = process_args(args)
|
||||
|
||||
parent = get_price_list_currency_and_exchange_rate(args)
|
||||
args.update(parent)
|
||||
|
||||
children = []
|
||||
|
||||
if "items" in args:
|
||||
@ -1064,7 +1070,7 @@ def get_price_list_currency_and_exchange_rate(args):
|
||||
return frappe._dict({
|
||||
"price_list_currency": price_list_currency,
|
||||
"price_list_uom_dependant": price_list_uom_dependant,
|
||||
"plc_conversion_rate": plc_conversion_rate
|
||||
"plc_conversion_rate": plc_conversion_rate or 1
|
||||
})
|
||||
|
||||
@frappe.whitelist()
|
||||
|
@ -55,19 +55,31 @@ def get_item_info(filters):
|
||||
|
||||
|
||||
def get_consumed_items(condition):
|
||||
purpose_to_exclude = [
|
||||
"Material Transfer for Manufacture",
|
||||
"Material Transfer",
|
||||
"Send to Subcontractor"
|
||||
]
|
||||
|
||||
condition += """
|
||||
and (
|
||||
purpose is NULL
|
||||
or purpose not in ({})
|
||||
)
|
||||
""".format(', '.join([f"'{p}'" for p in purpose_to_exclude]))
|
||||
condition = condition.replace("posting_date", "sle.posting_date")
|
||||
|
||||
consumed_items = frappe.db.sql("""
|
||||
select item_code, abs(sum(actual_qty)) as consumed_qty
|
||||
from `tabStock Ledger Entry`
|
||||
where actual_qty < 0
|
||||
from `tabStock Ledger Entry` as sle left join `tabStock Entry` as se
|
||||
on sle.voucher_no = se.name
|
||||
where
|
||||
actual_qty < 0
|
||||
and voucher_type not in ('Delivery Note', 'Sales Invoice')
|
||||
%s
|
||||
group by item_code
|
||||
""" % condition, as_dict=1)
|
||||
|
||||
consumed_items_map = {}
|
||||
for item in consumed_items:
|
||||
consumed_items_map.setdefault(item.item_code, item.consumed_qty)
|
||||
group by item_code""" % condition, as_dict=1)
|
||||
|
||||
consumed_items_map = {item.item_code : item.consumed_qty for item in consumed_items}
|
||||
return consumed_items_map
|
||||
|
||||
def get_delivered_items(condition):
|
||||
|
@ -1,15 +1,13 @@
|
||||
braintree==3.57.1
|
||||
frappe
|
||||
gocardless-pro==1.11.0
|
||||
googlemaps==3.1.1
|
||||
pandas>=1.0.5
|
||||
plaid-python>=7.0.0
|
||||
pycountry==19.8.18
|
||||
PyGithub==1.44.1
|
||||
python-stdnum==1.12
|
||||
python-youtube==0.6.0
|
||||
taxjar==1.9.0
|
||||
tweepy==3.8.0
|
||||
Unidecode==1.1.1
|
||||
WooCommerce==2.1.1
|
||||
pycryptodome==3.9.8
|
||||
gocardless-pro~=1.22.0
|
||||
googlemaps # used in ERPNext, but dependency is defined in Frappe
|
||||
pandas~=1.1.5
|
||||
plaid-python~=7.2.1
|
||||
pycountry~=20.7.3
|
||||
PyGithub~=1.54.1
|
||||
python-stdnum~=1.16
|
||||
python-youtube~=0.8.0
|
||||
taxjar~=1.9.2
|
||||
tweepy~=3.10.0
|
||||
Unidecode~=1.2.0
|
||||
WooCommerce~=3.0.0
|
||||
|
Loading…
Reference in New Issue
Block a user