Merge branch 'develop' into fix-stock-entry-feed

This commit is contained in:
Nabin Hait 2019-04-23 21:39:04 +05:30 committed by GitHub
commit 09438562d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
168 changed files with 28205 additions and 23747 deletions

View File

@ -5,7 +5,7 @@ import frappe
from erpnext.hooks import regional_overrides
from frappe.utils import getdate
__version__ = '11.1.17'
__version__ = '11.1.20'
def get_default_company(user=None):
'''Get default company for user'''

View File

@ -1,5 +1,7 @@
frappe.dashboard_chart_sources["Account Balance Timeline"] = {
method_path: "erpnext.accounts.dashboard_chart_source.account_balance_timeline.account_balance_timeline.get",
frappe.provide('frappe.dashboards.chart_sources');
frappe.dashboards.chart_sources["Account Balance Timeline"] = {
method: "erpnext.accounts.dashboard_chart_source.account_balance_timeline.account_balance_timeline.get",
filters: [
{
fieldname: "company",
@ -16,30 +18,5 @@ frappe.dashboard_chart_sources["Account Balance Timeline"] = {
options: "Account",
reqd: 1
},
{
fieldname: "timespan",
label: __("Period"),
fieldtype: "Select",
options: [
{value: "Last Year", label: __("Last Year")},
{value: "Last Quarter", label: __("Last Quarter")},
{value: "Last Month", label: __("Last Month")},
{value: "Last Week", label: __("Last Week")}
],
reqd: 1
},
{
fieldname: "timegrain",
label: __("Periodicity"),
fieldtype: "Select",
options: [
{value: "Quarterly", label: __("Quarterly")},
{value: "Monthly", label: __("Monthly")},
{value: "Weekly", label: __("Weekly")},
{value: "Daily", label: __("Daily")}
],
reqd: 1
},
],
is_time_series: true
]
};

View File

@ -1,13 +1,13 @@
{
"config": "{\n \"method_path\": \"erpnext.accounts.dashboard_chart_source.account_balance_timeline.account_balance_timeline.get\",\n\t\"filters\": [\n\t\t{\n\t\t\t\"fieldname\": \"company\",\n\t\t\t\"label\": \"Company\",\n\t\t\t\"fieldtype\": \"Link\",\n\t\t\t\"options\": \"Company\",\n\t\t\t\"reqd\": 1\n\t\t},\n\t\t{\n\t\t\t\"fieldname\": \"account\",\n\t\t\t\"label\": \"Account\",\n\t\t\t\"fieldtype\": \"Link\",\n\t\t\t\"options\": \"Account\",\n\t\t\t\"reqd\": 1\n\t\t},\n\t\t{\n\t\t\t\"fieldname\": \"timespan\",\n\t\t\t\"label\": \"Period\",\n\t\t\t\"fieldtype\": \"Select\",\n\t\t\t\"options\": [\n\t\t\t\t{\"value\": \"Last Year\", \"label\": \"Last Year\"},\n\t\t\t\t{\"value\": \"Last Quarter\", \"label\": \"Last Quarter\"},\n\t\t\t\t{\"value\": \"Last Month\", \"label\": \"Last Month\"},\n\t\t\t\t{\"value\": \"Last Week\", \"label\": \"Last Week\"}\n\t\t\t],\n\t\t\t\"reqd\": 1\n\t\t},\n\t\t{\n\t\t\t\"fieldname\": \"timegrain\",\n\t\t\t\"label\": \"Periodicity\",\n\t\t\t\"fieldtype\": \"Select\",\n\t\t\t\"options\": [\n\t\t\t\t{\"value\": \"Quarterly\", \"label\": \"Quarterly\"},\n\t\t\t\t{\"value\": \"Monthly\", \"label\": \"Monthly\"},\n\t\t\t\t{\"value\": \"Weekly\", \"label\": \"Weekly\"},\n\t\t\t\t{\"value\": \"Daily\", \"label\": \"Daily\"}\n\t\t\t],\n\t\t\t\"reqd\": 1\n\t\t}\n\t],\n\t\"is_time_series\": true\n}\n",
"creation": "2019-02-06 07:57:10.377718",
"docstatus": 0,
"doctype": "Dashboard Chart Source",
"idx": 0,
"modified": "2019-03-15 16:14:26.505986",
"modified": "2019-04-09 18:30:49.943174",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Account Balance Timeline",
"owner": "Administrator",
"source_name": "Account Balance Timeline"
"source_name": "Account Balance Timeline",
"timeseries": 1
}

View File

@ -2,31 +2,36 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
from itertools import groupby
from operator import itemgetter
import frappe
from frappe.core.page.dashboard.dashboard import cache_source
from frappe.utils import add_to_date, date_diff, getdate, nowdate
import frappe, json
from frappe.utils import add_to_date, date_diff, getdate, nowdate, get_last_day
from erpnext.accounts.report.general_ledger.general_ledger import execute
from frappe.core.page.dashboard.dashboard import cache_source, get_from_date_from_timespan
from frappe.desk.doctype.dashboard_chart.dashboard_chart import get_period_ending
from frappe.utils.nestedset import get_descendants_of
@frappe.whitelist()
@cache_source
def get(filters=None):
timespan = filters.get("timespan")
timegrain = filters.get("timegrain")
def get(chart_name=None, from_date = None, to_date = None):
chart = frappe.get_doc('Dashboard Chart', chart_name)
timespan = chart.timespan
timegrain = chart.time_interval
filters = json.loads(chart.filters_json)
account = filters.get("account")
company = filters.get("company")
from_date = get_from_date_from_timespan(timespan)
if not to_date:
to_date = nowdate()
if not from_date:
if timegrain in ('Monthly', 'Quarterly'):
from_date = get_from_date_from_timespan(to_date, timespan)
# fetch dates to plot
dates = get_dates_from_timegrain(from_date, to_date, timegrain)
# get all the entries for this account and its descendants
gl_entries = get_gl_entries(account, to_date)
gl_entries = get_gl_entries(account, get_period_ending(to_date, timegrain))
# compile balance values
result = build_result(account, dates, gl_entries)
@ -63,7 +68,7 @@ def build_result(account, dates, gl_entries):
# for balance sheet accounts, the totals are cumulative
if root_type in ('Asset', 'Liability', 'Equity'):
for i, r in enumerate(result):
if i < 0:
if i > 0:
r[1] = r[1] + result[i-1][1]
return result
@ -80,19 +85,6 @@ def get_gl_entries(account, to_date):
],
order_by = 'posting_date asc')
def get_from_date_from_timespan(timespan):
days = months = years = 0
if "Last Week" == timespan:
days = -7
if "Last Month" == timespan:
months = -1
elif "Last Quarter" == timespan:
months = -3
elif "Last Year" == timespan:
years = -1
return add_to_date(None, years=years, months=months, days=days,
as_string=True, as_datetime=True)
def get_dates_from_timegrain(from_date, to_date, timegrain):
days = months = years = 0
if "Daily" == timegrain:
@ -104,7 +96,8 @@ def get_dates_from_timegrain(from_date, to_date, timegrain):
elif "Quarterly" == timegrain:
months = 3
dates = [from_date]
while dates[-1] <= to_date:
dates.append(add_to_date(dates[-1], years=years, months=months, days=days))
dates = [get_period_ending(from_date, timegrain)]
while getdate(dates[-1]) < getdate(to_date):
date = get_period_ending(add_to_date(dates[-1], years=years, months=months, days=days), timegrain)
dates.append(date)
return dates

View File

@ -1,5 +1,6 @@
{
"allow_copy": 1,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@ -13,11 +14,14 @@
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Select account head of the bank where cheque was deposited.",
"fetch_from": "bank_account_no.account",
"fetch_if_empty": 1,
"fieldname": "bank_account",
"fieldtype": "Link",
"hidden": 0,
@ -40,14 +44,17 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "account_currency",
"fieldtype": "Link",
"hidden": 1,
@ -70,14 +77,17 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "from_date",
"fieldtype": "Date",
"hidden": 0,
@ -99,14 +109,17 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "to_date",
"fieldtype": "Date",
"hidden": 0,
@ -128,14 +141,83 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_5",
"fieldtype": "Column Break",
"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,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "bank_account_no",
"fieldtype": "Link",
"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": "Bank Account No",
"length": 0,
"no_copy": 0,
"options": "Bank Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "include_reconciled_entries",
"fieldtype": "Check",
"hidden": 0,
@ -157,14 +239,17 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "include_pos_transactions",
"fieldtype": "Check",
"hidden": 0,
@ -187,14 +272,17 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "get_payment_entries",
"fieldtype": "Button",
"hidden": 0,
@ -217,14 +305,49 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 1,
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "section_break_10",
"fieldtype": "Section Break",
"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,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 1,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "payment_entries",
"fieldtype": "Table",
"hidden": 0,
@ -247,14 +370,17 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "update_clearance_date",
"fieldtype": "Button",
"hidden": 0,
@ -277,14 +403,17 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "total_amount",
"fieldtype": "Currency",
"hidden": 0,
@ -307,22 +436,21 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 1,
"hide_toolbar": 1,
"icon": "fa fa-check",
"idx": 1,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2018-03-07 18:58:48.658687",
"modified": "2019-04-09 18:41:06.110453",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Reconciliation",
@ -330,7 +458,6 @@
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 0,
@ -351,9 +478,9 @@
],
"quick_entry": 1,
"read_only": 1,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_order": "ASC",
"track_changes": 0,
"track_seen": 0
"track_seen": 0,
"track_views": 0
}

View File

@ -19,8 +19,11 @@ class BankReconciliation(Document):
condition = ""
if not self.include_reconciled_entries:
condition = "and (clearance_date is null or clearance_date='0000-00-00')"
condition = " and (clearance_date is null or clearance_date='0000-00-00')"
account_cond = ""
if self.bank_account_no:
account_cond = " and t2.bank_account_no = {0}".format(frappe.db.escape(self.bank_account_no))
journal_entries = frappe.db.sql("""
select
@ -33,10 +36,13 @@ class BankReconciliation(Document):
where
t2.parent = t1.name and t2.account = %s and t1.docstatus=1
and t1.posting_date >= %s and t1.posting_date <= %s
and ifnull(t1.is_opening, 'No') = 'No' {0}
and ifnull(t1.is_opening, 'No') = 'No' {0} {1}
group by t2.account, t1.name
order by t1.posting_date ASC, t1.name DESC
""".format(condition), (self.bank_account, self.from_date, self.to_date), as_dict=1)
""".format(condition, account_cond), (self.bank_account, self.from_date, self.to_date), as_dict=1)
if self.bank_account_no:
condition = " and bank_account = %(bank_account_no)s"
payment_entries = frappe.db.sql("""
select
@ -53,7 +59,8 @@ class BankReconciliation(Document):
order by
posting_date ASC, name DESC
""".format(condition),
{"account":self.bank_account, "from":self.from_date, "to":self.to_date}, as_dict=1)
{"account":self.bank_account, "from":self.from_date,
"to":self.to_date, "bank_account_no": self.bank_account_no}, as_dict=1)
pos_entries = []
if self.include_pos_transactions:

View File

@ -18,7 +18,7 @@ def create_or_update_cheque_print_format(template_name):
"doc_type": "Payment Entry",
"standard": "No",
"custom_format": 1,
"print_format_type": "Server",
"print_format_type": "Jinja",
"name": template_name
})
else:

View File

@ -0,0 +1,177 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2019-03-07 12:07:09.416101",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "sales_invoice",
"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": "Invoice",
"length": 0,
"no_copy": 0,
"options": "Sales Invoice",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "sales_invoice.customer",
"fieldname": "customer",
"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": "Customer",
"length": 0,
"no_copy": 0,
"options": "Customer",
"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
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "sales_invoice.posting_date",
"fieldname": "posting_date",
"fieldtype": "Date",
"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": "Date",
"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
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "sales_invoice.grand_total",
"fieldname": "outstanding_amount",
"fieldtype": "Currency",
"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": "Outstanding Amount",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
"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
}
],
"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": "2019-03-07 16:38:03.622666",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Discounted Invoice",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class DiscountedInvoice(Document):
pass

View File

@ -7,6 +7,7 @@ from frappe import _
from frappe.utils import flt, fmt_money, getdate, formatdate
from frappe.model.document import Document
from frappe.model.naming import set_name_from_naming_options
from frappe.model.meta import get_field_precision
from erpnext.accounts.party import validate_party_gle_currency, validate_party_frozen_disabled
from erpnext.accounts.utils import get_account_currency
from erpnext.accounts.utils import get_fiscal_year
@ -63,7 +64,7 @@ class GLEntry(Document):
.format(self.voucher_type, self.voucher_no, self.account))
# Zero value transaction is not allowed
if not (flt(self.debit) or flt(self.credit)):
if not (flt(self.debit, self.precision("debit")) or flt(self.credit, self.precision("credit"))):
frappe.throw(_("{0} {1}: Either debit or credit amount is required for {2}")
.format(self.voucher_type, self.voucher_no, self.account))
@ -174,13 +175,20 @@ def update_outstanding_amt(account, party_type, party, against_voucher_type, aga
else:
party_condition = ""
if against_voucher_type == "Sales Invoice":
party_account = frappe.db.get_value(against_voucher_type, against_voucher, "debit_to")
account_condition = "and account in ({0}, {1})".format(frappe.db.escape(account), frappe.db.escape(party_account))
else:
account_condition = " and account = {0}".format(frappe.db.escape(account))
# get final outstanding amt
bal = flt(frappe.db.sql("""
select sum(debit_in_account_currency) - sum(credit_in_account_currency)
from `tabGL Entry`
where against_voucher_type=%s and against_voucher=%s
and account = %s {0}""".format(party_condition),
(against_voucher_type, against_voucher, account))[0][0] or 0.0)
and voucher_type != 'Invoice Discounting'
{0} {1}""".format(party_condition, account_condition),
(against_voucher_type, against_voucher))[0][0] or 0.0)
if against_voucher_type == 'Purchase Invoice':
bal = -bal
@ -223,17 +231,23 @@ def validate_frozen_account(account, adv_adj=None):
def update_against_account(voucher_type, voucher_no):
entries = frappe.db.get_all("GL Entry",
filters={"voucher_type": voucher_type, "voucher_no": voucher_no},
fields=["name", "party", "against", "debit", "credit", "account"])
fields=["name", "party", "against", "debit", "credit", "account", "company"])
if not entries:
return
company_currency = erpnext.get_company_currency(entries[0].company)
precision = get_field_precision(frappe.get_meta("GL Entry")
.get_field("debit"), company_currency)
accounts_debited, accounts_credited = [], []
for d in entries:
if flt(d.debit > 0): accounts_debited.append(d.party or d.account)
if flt(d.credit) > 0: accounts_credited.append(d.party or d.account)
if flt(d.debit, precision) > 0: accounts_debited.append(d.party or d.account)
if flt(d.credit, precision) > 0: accounts_credited.append(d.party or d.account)
for d in entries:
if flt(d.debit > 0):
if flt(d.debit, precision) > 0:
new_against = ", ".join(list(set(accounts_credited)))
if flt(d.credit > 0):
if flt(d.credit, precision) > 0:
new_against = ", ".join(list(set(accounts_debited)))
if d.against != new_against:

View File

@ -0,0 +1,197 @@
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Invoice Discounting', {
setup: (frm) => {
frm.set_query("sales_invoice", "invoices", (doc) => {
return {
"filters": {
"docstatus": 1,
"company": doc.company,
"outstanding_amount": [">", 0]
},
};
});
frm.events.filter_accounts("bank_account", frm, {"account_type": "Bank"});
frm.events.filter_accounts("bank_charges_account", frm, {"root_type": "Expense"});
frm.events.filter_accounts("short_term_loan", frm, {"root_type": "Liability"});
frm.events.filter_accounts("accounts_receivable_credit", frm, {"account_type": "Receivable"});
frm.events.filter_accounts("accounts_receivable_discounted", frm, {"account_type": "Receivable"});
frm.events.filter_accounts("accounts_receivable_unpaid", frm, {"account_type": "Receivable"});
},
filter_accounts: (fieldname, frm, addl_filters) => {
let filters = {
"company": frm.doc.company,
"is_group": 0
};
if(addl_filters) Object.assign(filters, addl_filters);
frm.set_query(fieldname, () => { return { "filters": filters }; });
},
refresh: (frm) => {
frm.events.show_general_ledger(frm);
if(frm.doc.docstatus === 0) {
frm.add_custom_button(__('Get Invoices'), function() {
frm.events.get_invoices(frm);
});
}
if(frm.doc.docstatus === 1 && frm.doc.status !== "Settled") {
if(frm.doc.status == "Sanctioned") {
frm.add_custom_button(__('Disburse Loan'), function() {
frm.events.create_disbursement_entry(frm);
}).addClass("btn-primary");
}
if(frm.doc.status == "Disbursed") {
frm.add_custom_button(__('Close Loan'), function() {
frm.events.close_loan(frm);
}).addClass("btn-primary");
}
}
},
loan_start_date: (frm) => {
frm.events.set_end_date(frm);
},
loan_period: (frm) => {
frm.events.set_end_date(frm);
},
set_end_date: (frm) => {
if(frm.doc.loan_start_date && frm.doc.loan_period) {
let end_date = frappe.datetime.add_days(frm.doc.loan_start_date, frm.doc.loan_period);
frm.set_value("loan_end_date", end_date);
}
},
validate: (frm) => {
frm.events.calculate_total_amount(frm);
},
calculate_total_amount: (frm) => {
let total_amount = 0.0;
for (let row of (frm.doc.invoices || [])) {
total_amount += flt(row.outstanding_amount);
}
frm.set_value("total_amount", total_amount);
},
get_invoices: (frm) => {
var d = new frappe.ui.Dialog({
title: __('Get Invoices based on Filters'),
fields: [
{
"label": "Customer",
"fieldname": "customer",
"fieldtype": "Link",
"options": "Customer"
},
{
"label": "From Date",
"fieldname": "from_date",
"fieldtype": "Date"
},
{
"label": "To Date",
"fieldname": "to_date",
"fieldtype": "Date"
},
{
"fieldname": "col_break",
"fieldtype": "Column Break",
},
{
"label": "Min Amount",
"fieldname": "min_amount",
"fieldtype": "Currency"
},
{
"label": "Max Amount",
"fieldname": "max_amount",
"fieldtype": "Currency"
}
],
primary_action: function() {
var data = d.get_values();
frappe.call({
method: "erpnext.accounts.doctype.invoice_discounting.invoice_discounting.get_invoices",
args: {
filters: data
},
callback: function(r) {
if(!r.exc) {
d.hide();
$.each(r.message, function(i, v) {
frm.doc.invoices = frm.doc.invoices.filter(row => row.sales_invoice);
let row = frm.add_child("invoices");
$.extend(row, v);
});
refresh_field("invoices");
}
}
});
},
primary_action_label: __('Get Invocies')
});
d.show();
},
create_disbursement_entry: (frm) => {
frappe.call({
method:"create_disbursement_entry",
doc: frm.doc,
callback: function(r) {
if(!r.exc){
var doclist = frappe.model.sync(r.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
}
}
});
},
close_loan: (frm) => {
frappe.call({
method:"close_loan",
doc: frm.doc,
callback: function(r) {
if(!r.exc){
var doclist = frappe.model.sync(r.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
}
}
});
},
show_general_ledger: (frm) => {
if(frm.doc.docstatus===1) {
cur_frm.add_custom_button(__('Accounting Ledger'), function() {
frappe.route_options = {
voucher_no: frm.doc.name,
from_date: frm.doc.posting_date,
to_date: frm.doc.posting_date,
company: frm.doc.company,
group_by: "Group by Voucher (Consolidated)"
};
frappe.set_route("query-report", "General Ledger");
}, __("View"));
}
}
});
frappe.ui.form.on('Discounted Invoice', {
sales_invoice: (frm) => {
frm.events.calculate_total_amount(frm);
},
invoices_remove: (frm) => {
frm.events.calculate_total_amount(frm);
}
});

View File

@ -0,0 +1,773 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
"autoname": "ACC-INV-DISC-.YYYY.-.#####",
"beta": 0,
"creation": "2019-03-07 12:01:56.296952",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "posting_date",
"fieldtype": "Date",
"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": "Posting Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "loan_start_date",
"fieldtype": "Date",
"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": "Loan Start Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "loan_period",
"fieldtype": "Int",
"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": "Loan Period",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "loan_end_date",
"fieldtype": "Date",
"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": "Loan End Date",
"length": 0,
"no_copy": 1,
"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
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"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,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "status",
"fieldtype": "Select",
"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": "Status",
"length": 0,
"no_copy": 1,
"options": "Draft\nSanctioned\nDisbursed\nSettled\nCancelled",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company",
"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": "Company",
"length": 0,
"no_copy": 0,
"options": "Company",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_5",
"fieldtype": "Section Break",
"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,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "invoices",
"fieldtype": "Table",
"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": "Invoices",
"length": 0,
"no_copy": 0,
"options": "Discounted Invoice",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_7",
"fieldtype": "Section Break",
"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,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "total_amount",
"fieldtype": "Currency",
"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": "Total Amount",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
"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
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_9",
"fieldtype": "Column Break",
"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,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "bank_charges",
"fieldtype": "Currency",
"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": "Bank Charges",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_6",
"fieldtype": "Section Break",
"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,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "short_term_loan",
"fieldtype": "Link",
"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": "Short Term Loan Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "bank_account",
"fieldtype": "Link",
"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": "Bank Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "bank_charges_account",
"fieldtype": "Link",
"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": "Bank Charges Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_15",
"fieldtype": "Column Break",
"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,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "accounts_receivable_credit",
"fieldtype": "Link",
"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": "Accounts Receivable Credit Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "accounts_receivable_discounted",
"fieldtype": "Link",
"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": "Accounts Receivable Discounted Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "accounts_receivable_unpaid",
"fieldtype": "Link",
"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": "Accounts Receivable Unpaid Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"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": "Amended From",
"length": 0,
"no_copy": 1,
"options": "Invoice Discounting",
"permlevel": 0,
"print_hide": 1,
"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
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-03-08 14:24:31.222027",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Invoice Discounting",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}

View File

@ -0,0 +1,219 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe, json
from frappe import _
from frappe.utils import flt, getdate, nowdate, add_days
from erpnext.controllers.accounts_controller import AccountsController
from erpnext.accounts.general_ledger import make_gl_entries
class InvoiceDiscounting(AccountsController):
def validate(self):
self.validate_mandatory()
self.calculate_total_amount()
self.set_status()
self.set_end_date()
def set_end_date(self):
if self.loan_start_date and self.loan_period:
self.loan_end_date = add_days(self.loan_start_date, self.loan_period)
def validate_mandatory(self):
if self.docstatus == 1 and not (self.loan_start_date and self.loan_period):
frappe.throw(_("Loan Start Date and Loan Period are mandatory to save the Invoice Discounting"))
def calculate_total_amount(self):
self.total_amount = sum([flt(d.outstanding_amount) for d in self.invoices])
def on_submit(self):
self.make_gl_entries()
def on_cancel(self):
self.set_status()
self.make_gl_entries()
def set_status(self):
self.status = "Draft"
if self.docstatus == 1:
self.status = "Sanctioned"
elif self.docstatus == 2:
self.status = "Cancelled"
def make_gl_entries(self):
company_currency = frappe.get_cached_value('Company', self.company, "default_currency")
gl_entries = []
for d in self.invoices:
inv = frappe.db.get_value("Sales Invoice", d.sales_invoice,
["debit_to", "party_account_currency", "conversion_rate", "cost_center"], as_dict=1)
if d.outstanding_amount:
outstanding_in_company_currency = flt(d.outstanding_amount * inv.conversion_rate,
d.precision("outstanding_amount"))
ar_credit_account_currency = frappe.get_cached_value("Account", self.accounts_receivable_credit, "currency")
gl_entries.append(self.get_gl_dict({
"account": inv.debit_to,
"party_type": "Customer",
"party": d.customer,
"against": self.accounts_receivable_credit,
"credit": outstanding_in_company_currency,
"credit_in_account_currency": outstanding_in_company_currency \
if inv.party_account_currency==company_currency else d.outstanding_amount,
"cost_center": inv.cost_center,
"against_voucher": d.sales_invoice,
"against_voucher_type": "Sales Invoice"
}, inv.party_account_currency))
gl_entries.append(self.get_gl_dict({
"account": self.accounts_receivable_credit,
"party_type": "Customer",
"party": d.customer,
"against": inv.debit_to,
"debit": outstanding_in_company_currency,
"debit_in_account_currency": outstanding_in_company_currency \
if ar_credit_account_currency==company_currency else d.outstanding_amount,
"cost_center": inv.cost_center,
"against_voucher": d.sales_invoice,
"against_voucher_type": "Sales Invoice"
}, ar_credit_account_currency))
make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding='No')
def create_disbursement_entry(self):
je = frappe.new_doc("Journal Entry")
je.voucher_type = 'Journal Entry'
je.company = self.company
je.remark = 'Loan Disbursement entry against Invoice Discounting: ' + self.name
je.append("accounts", {
"account": self.bank_account,
"debit_in_account_currency": flt(self.total_amount) - flt(self.bank_charges),
})
je.append("accounts", {
"account": self.bank_charges_account,
"debit_in_account_currency": flt(self.bank_charges)
})
je.append("accounts", {
"account": self.short_term_loan,
"credit_in_account_currency": flt(self.total_amount),
"reference_type": "Invoice Discounting",
"reference_name": self.name
})
for d in self.invoices:
je.append("accounts", {
"account": self.accounts_receivable_discounted,
"debit_in_account_currency": flt(d.outstanding_amount),
"reference_type": "Invoice Discounting",
"reference_name": self.name,
"party_type": "Customer",
"party": d.customer
})
je.append("accounts", {
"account": self.accounts_receivable_credit,
"credit_in_account_currency": flt(d.outstanding_amount),
"reference_type": "Invoice Discounting",
"reference_name": self.name,
"party_type": "Customer",
"party": d.customer
})
return je
def close_loan(self):
je = frappe.new_doc("Journal Entry")
je.voucher_type = 'Journal Entry'
je.company = self.company
je.remark = 'Loan Settlement entry against Invoice Discounting: ' + self.name
je.append("accounts", {
"account": self.short_term_loan,
"debit_in_account_currency": flt(self.total_amount),
"reference_type": "Invoice Discounting",
"reference_name": self.name,
})
je.append("accounts", {
"account": self.bank_account,
"credit_in_account_currency": flt(self.total_amount)
})
if getdate(self.loan_end_date) > getdate(nowdate()):
for d in self.invoices:
outstanding_amount = frappe.db.get_value("Sales Invoice", d.sales_invoice, "outstanding_amount")
if flt(outstanding_amount) > 0:
je.append("accounts", {
"account": self.accounts_receivable_discounted,
"credit_in_account_currency": flt(outstanding_amount),
"reference_type": "Invoice Discounting",
"reference_name": self.name,
"party_type": "Customer",
"party": d.customer
})
je.append("accounts", {
"account": self.accounts_receivable_unpaid,
"debit_in_account_currency": flt(outstanding_amount),
"reference_type": "Invoice Discounting",
"reference_name": self.name,
"party_type": "Customer",
"party": d.customer
})
return je
@frappe.whitelist()
def get_invoices(filters):
filters = frappe._dict(json.loads(filters))
cond = []
if filters.customer:
cond.append("customer=%(customer)s")
if filters.from_date:
cond.append("posting_date >= %(from_date)s")
if filters.to_date:
cond.append("posting_date <= %(to_date)s")
if filters.min_amount:
cond.append("base_grand_total >= %(min_amount)s")
if filters.max_amount:
cond.append("base_grand_total <= %(max_amount)s")
where_condition = ""
if cond:
where_condition += " and " + " and ".join(cond)
return frappe.db.sql("""
select
name as sales_invoice,
customer,
posting_date,
outstanding_amount
from `tabSales Invoice` si
where
docstatus = 1
and outstanding_amount > 0
%s
and not exists(select di.name from `tabDiscounted Invoice` di
where di.docstatus=1 and di.sales_invoice=si.name)
""" % where_condition, filters, as_dict=1)
def get_party_account_based_on_invoice_discounting(sales_invoice):
party_account = None
invoice_discounting = frappe.db.sql("""
select par.accounts_receivable_discounted, par.accounts_receivable_unpaid, par.status
from `tabInvoice Discounting` par, `tabDiscounted Invoice` ch
where par.name=ch.parent
and par.docstatus=1
and ch.sales_invoice = %s
""", (sales_invoice), as_dict=1)
if invoice_discounting:
if invoice_discounting[0].status == "Disbursed":
party_account = invoice_discounting[0].accounts_receivable_discounted
elif invoice_discounting[0].status == "Settled":
party_account = invoice_discounting[0].accounts_receivable_unpaid
return party_account

View File

@ -0,0 +1,20 @@
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'reference_name',
'internal_links': {
'Sales Invoice': ['invoices', 'sales_invoice']
},
'transactions': [
{
'label': _('Reference'),
'items': ['Sales Invoice']
},
{
'label': _('Payment'),
'items': ['Payment Entry', 'Journal Entry']
}
]
}

View File

@ -0,0 +1,21 @@
frappe.listview_settings['Invoice Discounting'] = {
add_fields: ["status"],
get_indicator: function(doc)
{
if(doc.status == "Draft") {
return [__("Draft"), "red", "status,=,Draft"];
}
else if(doc.status == "Sanctioned") {
return [__("Sanctioned"), "green", "status,=,Sanctioned"];
}
else if(doc.status == "Disbursed") {
return [__("Disbursed"), "blue", "status,=,Disbursed"];
}
else if(doc.status == "Settled") {
return [__("Settled"), "orange", "status,=,Settled"];
}
else if(doc.status == "Canceled") {
return [__("Canceled"), "red", "status,=,Canceled"];
}
}
};

View File

@ -0,0 +1,302 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
from frappe.utils import nowdate, add_days, flt
import unittest
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries
from erpnext.accounts.doctype.account.test_account import create_account
from erpnext.accounts.doctype.journal_entry.journal_entry import get_payment_entry_against_invoice
class TestInvoiceDiscounting(unittest.TestCase):
def setUp(self):
self.ar_credit = create_account(account_name="_Test Accounts Receivable Credit", parent_account = "Accounts Receivable - _TC", company="_Test Company")
self.ar_discounted = create_account(account_name="_Test Accounts Receivable Discounted", parent_account = "Accounts Receivable - _TC", company="_Test Company")
self.ar_unpaid = create_account(account_name="_Test Accounts Receivable Unpaid", parent_account = "Accounts Receivable - _TC", company="_Test Company")
self.short_term_loan = create_account(account_name="_Test Short Term Loan", parent_account = "Source of Funds (Liabilities) - _TC", company="_Test Company")
self.bank_account = create_account(account_name="_Test Bank 2", parent_account = "Bank Accounts - _TC", company="_Test Company")
self.bank_charges_account = create_account(account_name="_Test Bank Charges Account", parent_account = "Expenses - _TC", company="_Test Company")
frappe.db.set_value("Company", "_Test Company", "default_bank_account", self.bank_account)
def test_total_amount(self):
inv1 = create_sales_invoice(rate=200)
inv2 = create_sales_invoice(rate=500)
inv_disc = create_invoice_discounting([inv1.name, inv2.name],
do_not_submit=True,
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account
)
self.assertEqual(inv_disc.total_amount, 700)
def test_gl_entries_in_base_currency(self):
inv = create_sales_invoice(rate=200)
inv_disc = create_invoice_discounting([inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account
)
gle = get_gl_entries("Invoice Discounting", inv_disc.name)
expected_gle = {
inv.debit_to: [0.0, 200],
self.ar_credit: [200, 0.0]
}
for i, gle in enumerate(gle):
self.assertEqual([gle.debit, gle.credit], expected_gle.get(gle.account))
def test_loan_on_submit(self):
inv = create_sales_invoice(rate=300)
inv_disc = create_invoice_discounting([inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account,
start=nowdate(),
period=60
)
self.assertEqual(inv_disc.status, "Sanctioned")
self.assertEqual(inv_disc.loan_end_date, add_days(inv_disc.loan_start_date, inv_disc.loan_period))
def test_on_disbursed(self):
inv = create_sales_invoice(rate=500)
inv_disc = create_invoice_discounting([inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account,
)
je = inv_disc.create_disbursement_entry()
self.assertEqual(je.accounts[0].account, self.bank_account)
self.assertEqual(je.accounts[0].debit_in_account_currency, flt(inv_disc.total_amount) - flt(inv_disc.bank_charges))
self.assertEqual(je.accounts[1].account, self.bank_charges_account)
self.assertEqual(je.accounts[1].debit_in_account_currency, flt(inv_disc.bank_charges))
self.assertEqual(je.accounts[2].account, self.short_term_loan)
self.assertEqual(je.accounts[2].credit_in_account_currency, flt(inv_disc.total_amount))
self.assertEqual(je.accounts[3].account, self.ar_discounted)
self.assertEqual(je.accounts[3].debit_in_account_currency, flt(inv.outstanding_amount))
self.assertEqual(je.accounts[4].account, self.ar_credit)
self.assertEqual(je.accounts[4].credit_in_account_currency, flt(inv.outstanding_amount))
je.posting_date = nowdate()
je.submit()
inv_disc.reload()
self.assertEqual(inv_disc.status, "Disbursed")
inv.reload()
self.assertEqual(inv.outstanding_amount, 500)
def test_on_close_after_loan_period(self):
inv = create_sales_invoice(rate=600)
inv_disc = create_invoice_discounting([inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account,
start=nowdate(),
period=60
)
je1 = inv_disc.create_disbursement_entry()
je1.posting_date = nowdate()
je1.submit()
je2 = inv_disc.close_loan()
self.assertEqual(je2.accounts[0].account, self.short_term_loan)
self.assertEqual(je2.accounts[0].debit_in_account_currency, flt(inv_disc.total_amount))
self.assertEqual(je2.accounts[1].account, self.bank_account)
self.assertEqual(je2.accounts[1].credit_in_account_currency, flt(inv_disc.total_amount))
self.assertEqual(je2.accounts[2].account, self.ar_discounted)
self.assertEqual(je2.accounts[2].credit_in_account_currency, flt(inv.outstanding_amount))
self.assertEqual(je2.accounts[3].account, self.ar_unpaid)
self.assertEqual(je2.accounts[3].debit_in_account_currency, flt(inv.outstanding_amount))
je2.posting_date = nowdate()
je2.submit()
inv_disc.reload()
self.assertEqual(inv_disc.status, "Settled")
def test_on_close_after_loan_period_after_inv_payment(self):
inv = create_sales_invoice(rate=600)
inv_disc = create_invoice_discounting([inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account,
start=nowdate(),
period=60
)
je1 = inv_disc.create_disbursement_entry()
je1.posting_date = nowdate()
je1.submit()
je_on_payment = frappe.get_doc(get_payment_entry_against_invoice("Sales Invoice", inv.name))
je_on_payment.posting_date = nowdate()
je_on_payment.cheque_no = "126981"
je_on_payment.cheque_date = nowdate()
je_on_payment.save()
je_on_payment.submit()
je2 = inv_disc.close_loan()
self.assertEqual(je2.accounts[0].account, self.short_term_loan)
self.assertEqual(je2.accounts[0].debit_in_account_currency, flt(inv_disc.total_amount))
self.assertEqual(je2.accounts[1].account, self.bank_account)
self.assertEqual(je2.accounts[1].credit_in_account_currency, flt(inv_disc.total_amount))
def test_on_close_before_loan_period(self):
inv = create_sales_invoice(rate=700)
inv_disc = create_invoice_discounting([inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account,
start=add_days(nowdate(), -80),
period=60
)
je1 = inv_disc.create_disbursement_entry()
je1.posting_date = nowdate()
je1.submit()
je2 = inv_disc.close_loan()
je2.posting_date = nowdate()
je2.submit()
self.assertEqual(je2.accounts[0].account, self.short_term_loan)
self.assertEqual(je2.accounts[0].debit_in_account_currency, flt(inv_disc.total_amount))
self.assertEqual(je2.accounts[1].account, self.bank_account)
self.assertEqual(je2.accounts[1].credit_in_account_currency, flt(inv_disc.total_amount))
def test_make_payment_before_loan_period(self):
#it has problem
inv = create_sales_invoice(rate=700)
inv_disc = create_invoice_discounting([inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account
)
je = inv_disc.create_disbursement_entry()
inv_disc.reload()
je.posting_date = nowdate()
je.submit()
je_on_payment = frappe.get_doc(get_payment_entry_against_invoice("Sales Invoice", inv.name))
je_on_payment.posting_date = nowdate()
je_on_payment.cheque_no = "126981"
je_on_payment.cheque_date = nowdate()
je_on_payment.save()
je_on_payment.submit()
self.assertEqual(je_on_payment.accounts[0].account, self.ar_discounted)
self.assertEqual(je_on_payment.accounts[0].credit_in_account_currency, flt(inv.outstanding_amount))
self.assertEqual(je_on_payment.accounts[1].account, self.bank_account)
self.assertEqual(je_on_payment.accounts[1].debit_in_account_currency, flt(inv.outstanding_amount))
inv.reload()
self.assertEqual(inv.outstanding_amount, 0)
def test_make_payment_before_after_period(self):
#it has problem
inv = create_sales_invoice(rate=700)
inv_disc = create_invoice_discounting([inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account,
loan_start_date=add_days(nowdate(), -10),
period=5
)
je = inv_disc.create_disbursement_entry()
inv_disc.reload()
je.posting_date = nowdate()
je.submit()
je = inv_disc.close_loan()
inv_disc.reload()
je.posting_date = nowdate()
je.submit()
je_on_payment = frappe.get_doc(get_payment_entry_against_invoice("Sales Invoice", inv.name))
je_on_payment.posting_date = nowdate()
je_on_payment.cheque_no = "126981"
je_on_payment.cheque_date = nowdate()
je_on_payment.submit()
self.assertEqual(je_on_payment.accounts[0].account, self.ar_unpaid)
self.assertEqual(je_on_payment.accounts[0].credit_in_account_currency, flt(inv.outstanding_amount))
self.assertEqual(je_on_payment.accounts[1].account, self.bank_account)
self.assertEqual(je_on_payment.accounts[1].debit_in_account_currency, flt(inv.outstanding_amount))
inv.reload()
self.assertEqual(inv.outstanding_amount, 0)
def create_invoice_discounting(invoices, **args):
args = frappe._dict(args)
inv_disc = frappe.new_doc("Invoice Discounting")
inv_disc.posting_date = args.posting_date or nowdate()
inv_disc.company = args.company or "_Test Company"
inv_disc.bank_account = args.bank_account
inv_disc.short_term_loan = args.short_term_loan
inv_disc.accounts_receivable_credit = args.accounts_receivable_credit
inv_disc.accounts_receivable_discounted = args.accounts_receivable_discounted
inv_disc.accounts_receivable_unpaid = args.accounts_receivable_unpaid
inv_disc.short_term_loan=args.short_term_loan
inv_disc.bank_charges_account=args.bank_charges_account
inv_disc.bank_account=args.bank_account
inv_disc.loan_start_date = args.start or nowdate()
inv_disc.loan_period = args.period or 30
for d in invoices:
inv_disc.append("invoices", {
"sales_invoice": d
})
inv_disc.insert()
if not args.do_not_submit:
inv_disc.submit()
return inv_disc

View File

@ -6,6 +6,10 @@ frappe.provide("erpnext.journal_entry");
frappe.ui.form.on("Journal Entry", {
setup: function(frm) {
frm.add_fetch("bank_account_no", "account", "account");
},
refresh: function(frm) {
erpnext.toggle_naming_series();
frm.cscript.voucher_type(frm.doc);

View File

@ -3,13 +3,14 @@
from __future__ import unicode_literals
import frappe, erpnext, json
from frappe.utils import cstr, flt, fmt_money, formatdate, getdate, nowdate, cint
from frappe.utils import cstr, flt, fmt_money, formatdate, getdate, nowdate, cint, get_link_to_form
from frappe import msgprint, _, scrub
from erpnext.controllers.accounts_controller import AccountsController
from erpnext.accounts.utils import get_balance_on, get_account_currency
from erpnext.accounts.party import get_party_account
from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
from erpnext.hr.doctype.loan.loan import update_disbursement_status, update_total_amount_paid
from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import get_party_account_based_on_invoice_discounting
from six import string_types, iteritems
@ -51,7 +52,22 @@ class JournalEntry(AccountsController):
self.update_expense_claim()
self.update_loan()
self.update_inter_company_jv()
self.update_invoice_discounting()
def on_cancel(self):
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
from erpnext.hr.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip
unlink_ref_doc_from_payment_entries(self)
unlink_ref_doc_from_salary_slip(self.name)
self.make_gl_entries(1)
self.update_advance_paid()
self.update_expense_claim()
self.update_loan()
self.unlink_advance_entry_reference()
self.unlink_asset_reference()
self.unlink_inter_company_jv()
self.unlink_asset_adjustment_entry()
self.update_invoice_discounting()
def get_title(self):
return self.pay_to_recd_from or self.accounts[0].account
@ -81,19 +97,32 @@ class JournalEntry(AccountsController):
frappe.db.set_value("Journal Entry", self.inter_company_journal_entry_reference,\
"inter_company_journal_entry_reference", self.name)
def on_cancel(self):
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
from erpnext.hr.doctype.salary_slip.salary_slip import unlink_ref_doc_from_salary_slip
unlink_ref_doc_from_payment_entries(self)
unlink_ref_doc_from_salary_slip(self.name)
self.make_gl_entries(1)
self.update_advance_paid()
self.update_expense_claim()
self.update_loan()
self.unlink_advance_entry_reference()
self.unlink_asset_reference()
self.unlink_inter_company_jv()
self.unlink_asset_adjustment_entry()
def update_invoice_discounting(self):
def _validate_invoice_discounting_status(inv_disc, id_status, expected_status, row_id):
id_link = get_link_to_form("Invoice Discounting", inv_disc)
if id_status != expected_status:
frappe.throw(_("Row #{0}: Status must be {1} for Invoice Discounting {2}").format(d.idx, expected_status, id_link))
invoice_discounting_list = list(set([d.reference_name for d in self.accounts if d.reference_type=="Invoice Discounting"]))
for inv_disc in invoice_discounting_list:
short_term_loan_account, id_status = frappe.db.get_value("Invoice Discounting", inv_disc, ["short_term_loan", "status"])
for d in self.accounts:
if d.account == short_term_loan_account and d.reference_name == inv_disc:
if self.docstatus == 1:
if d.credit > 0:
_validate_invoice_discounting_status(inv_disc, id_status, "Sanctioned", d.idx)
status = "Disbursed"
elif d.debit > 0:
_validate_invoice_discounting_status(inv_disc, id_status, "Disbursed", d.idx)
status = "Settled"
else:
if d.credit > 0:
_validate_invoice_discounting_status(inv_disc, id_status, "Disbursed", d.idx)
status = "Sanctioned"
elif d.debit > 0:
_validate_invoice_discounting_status(inv_disc, id_status, "Settled", d.idx)
status = "Disbursed"
frappe.db.set_value("Invoice Discounting", inv_disc, "status", status)
def unlink_advance_entry_reference(self):
for d in self.get("accounts"):
@ -246,7 +275,11 @@ class JournalEntry(AccountsController):
# check if party and account match
if d.reference_type in ("Sales Invoice", "Purchase Invoice"):
if (against_voucher[0] != d.party or against_voucher[1] != d.account):
if d.reference_type == "Sales Invoice":
party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1]
else:
party_account = against_voucher[1]
if (against_voucher[0] != d.party or party_account != d.account):
frappe.throw(_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}")
.format(d.idx, field_dict.get(d.reference_type)[0], field_dict.get(d.reference_type)[1],
d.reference_type, d.reference_name))
@ -688,7 +721,7 @@ def get_payment_entry_against_invoice(dt, dn, amount=None, debit_in_account_cur
ref_doc = frappe.get_doc(dt, dn)
if dt == "Sales Invoice":
party_type = "Customer"
party_account = ref_doc.debit_to
party_account = get_party_account_based_on_invoice_discounting(dn) or ref_doc.debit_to
else:
party_type = "Supplier"
party_account = ref_doc.credit_to
@ -715,7 +748,6 @@ def get_payment_entry_against_invoice(dt, dn, amount=None, debit_in_account_cur
"journal_entry": journal_entry
})
def get_payment_entry(ref_doc, args):
cost_center = ref_doc.get("cost_center") or frappe.get_cached_value('Company', ref_doc.company, "cost_center")
exchange_rate = 1

View File

@ -21,6 +21,7 @@
"bold": 1,
"collapsible": 0,
"columns": 3,
"fetch_if_empty": 0,
"fieldname": "account",
"fieldtype": "Link",
"hidden": 0,
@ -57,6 +58,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "account_type",
"fieldtype": "Data",
"hidden": 1,
@ -89,6 +91,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "balance",
"fieldtype": "Currency",
"hidden": 0,
@ -125,6 +128,7 @@
"columns": 0,
"default": ":Company",
"description": "If Income or Expense",
"fetch_if_empty": 0,
"fieldname": "cost_center",
"fieldtype": "Link",
"hidden": 0,
@ -161,6 +165,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "col_break1",
"fieldtype": "Column Break",
"hidden": 0,
@ -191,6 +196,41 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "bank_account_no",
"fieldtype": "Link",
"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": "Bank Account No",
"length": 0,
"no_copy": 0,
"options": "Bank Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "party_type",
"fieldtype": "Link",
"hidden": 0,
@ -223,6 +263,7 @@
"bold": 0,
"collapsible": 0,
"columns": 3,
"fetch_if_empty": 0,
"fieldname": "party",
"fieldtype": "Dynamic Link",
"hidden": 0,
@ -255,6 +296,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "party_balance",
"fieldtype": "Currency",
"hidden": 0,
@ -290,6 +332,7 @@
"collapsible_depends_on": "",
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "currency_section",
"fieldtype": "Section Break",
"hidden": 0,
@ -322,6 +365,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "account_currency",
"fieldtype": "Link",
"hidden": 0,
@ -355,6 +399,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_10",
"fieldtype": "Column Break",
"hidden": 0,
@ -386,6 +431,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "exchange_rate",
"fieldtype": "Float",
"hidden": 0,
@ -418,6 +464,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "sec_break1",
"fieldtype": "Section Break",
"hidden": 0,
@ -449,6 +496,7 @@
"bold": 1,
"collapsible": 0,
"columns": 2,
"fetch_if_empty": 0,
"fieldname": "debit_in_account_currency",
"fieldtype": "Currency",
"hidden": 0,
@ -482,6 +530,7 @@
"bold": 1,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "debit",
"fieldtype": "Currency",
"hidden": 0,
@ -517,6 +566,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "col_break2",
"fieldtype": "Column Break",
"hidden": 0,
@ -547,6 +597,7 @@
"bold": 1,
"collapsible": 0,
"columns": 2,
"fetch_if_empty": 0,
"fieldname": "credit_in_account_currency",
"fieldtype": "Currency",
"hidden": 0,
@ -580,6 +631,7 @@
"bold": 1,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "credit",
"fieldtype": "Currency",
"hidden": 0,
@ -615,6 +667,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "reference",
"fieldtype": "Section Break",
"hidden": 0,
@ -646,6 +699,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "reference_type",
"fieldtype": "Select",
"hidden": 0,
@ -658,7 +712,7 @@
"label": "Reference Type",
"length": 0,
"no_copy": 0,
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation",
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@ -679,6 +733,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "reference_name",
"fieldtype": "Dynamic Link",
"hidden": 0,
@ -713,6 +768,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.reference_type&&!in_list(doc.reference_type, ['Expense Claim', 'Asset', 'Employee Loan', 'Employee Advance'])",
"fetch_if_empty": 0,
"fieldname": "reference_due_date",
"fieldtype": "Select",
"hidden": 0,
@ -746,6 +802,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "project",
"fieldtype": "Link",
"hidden": 0,
@ -779,6 +836,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "col_break3",
"fieldtype": "Column Break",
"hidden": 0,
@ -809,6 +867,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "is_advance",
"fieldtype": "Select",
"hidden": 0,
@ -843,6 +902,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "user_remark",
"fieldtype": "Small Text",
"hidden": 0,
@ -875,6 +935,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "against_account",
"fieldtype": "Text",
"hidden": 1,
@ -903,16 +964,14 @@
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 1,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2019-02-18 19:00:53.662788",
"modified": "2019-04-15 18:48:29.494134",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Account",
@ -920,10 +979,9 @@
"permissions": [],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}
}

View File

@ -232,6 +232,13 @@ frappe.ui.form.on('Payment Entry', {
},
party_type: function(frm) {
let party_types = Object.keys(frappe.boot.party_account_types);
if(frm.doc.party_type && !party_types.includes(frm.doc.party_type)){
frm.set_value("party_type", "");
frappe.throw(__("Party can only be one of "+ party_types.join(", ")));
}
if(frm.doc.party) {
$.each(["party", "party_balance", "paid_from", "paid_to",
"paid_from_account_currency", "paid_from_account_balance",

View File

@ -20,6 +20,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "type_of_payment",
"fieldtype": "Section Break",
"hidden": 0,
@ -53,6 +54,7 @@
"collapsible": 0,
"columns": 0,
"default": "",
"fetch_if_empty": 0,
"fieldname": "naming_series",
"fieldtype": "Select",
"hidden": 0,
@ -86,6 +88,7 @@
"bold": 1,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "payment_type",
"fieldtype": "Select",
"hidden": 0,
@ -119,6 +122,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_5",
"fieldtype": "Column Break",
"hidden": 0,
@ -151,6 +155,7 @@
"collapsible": 0,
"columns": 0,
"default": "Today",
"fetch_if_empty": 0,
"fieldname": "posting_date",
"fieldtype": "Date",
"hidden": 0,
@ -183,6 +188,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
@ -216,6 +222,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "cost_center",
"fieldtype": "Link",
"hidden": 0,
@ -249,6 +256,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "mode_of_payment",
"fieldtype": "Link",
"hidden": 0,
@ -283,6 +291,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:in_list([\"Receive\", \"Pay\"], doc.payment_type)",
"fetch_if_empty": 0,
"fieldname": "party_section",
"fieldtype": "Section Break",
"hidden": 0,
@ -317,6 +326,7 @@
"columns": 0,
"default": "",
"depends_on": "eval:in_list([\"Receive\", \"Pay\"], doc.payment_type) && doc.docstatus==0",
"fetch_if_empty": 0,
"fieldname": "party_type",
"fieldtype": "Link",
"hidden": 0,
@ -351,6 +361,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:in_list([\"Receive\", \"Pay\"], doc.payment_type) && doc.party_type",
"fetch_if_empty": 0,
"fieldname": "party",
"fieldtype": "Dynamic Link",
"hidden": 0,
@ -386,6 +397,7 @@
"columns": 0,
"depends_on": "eval:in_list([\"Receive\", \"Pay\"], doc.payment_type) && doc.party_type",
"description": "",
"fetch_if_empty": 0,
"fieldname": "party_name",
"fieldtype": "Data",
"hidden": 0,
@ -418,6 +430,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_11",
"fieldtype": "Column Break",
"hidden": 0,
@ -450,6 +463,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "party",
"fetch_if_empty": 0,
"fieldname": "bank_account",
"fieldtype": "Link",
"hidden": 0,
@ -476,40 +490,6 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "contact_person",
"fieldname": "contact_email",
"fieldtype": "Data",
"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": "Email",
"length": 0,
"no_copy": 0,
"options": "Email",
"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
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@ -518,6 +498,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "party",
"fetch_if_empty": 0,
"fieldname": "contact_person",
"fieldtype": "Link",
"hidden": 0,
@ -544,6 +525,41 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "contact_person",
"fetch_if_empty": 0,
"fieldname": "contact_email",
"fieldtype": "Data",
"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": "Email",
"length": 0,
"no_copy": 0,
"options": "Email",
"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
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@ -551,6 +567,7 @@
"bold": 0,
"collapsible": 1,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "payment_accounts_section",
"fieldtype": "Section Break",
"hidden": 0,
@ -584,6 +601,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "party",
"fetch_if_empty": 0,
"fieldname": "party_balance",
"fieldtype": "Currency",
"hidden": 0,
@ -617,6 +635,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:(in_list([\"Internal Transfer\", \"Pay\"], doc.payment_type) || doc.party)",
"fetch_if_empty": 0,
"fieldname": "paid_from",
"fieldtype": "Link",
"hidden": 0,
@ -651,6 +670,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "paid_from",
"fetch_if_empty": 0,
"fieldname": "paid_from_account_currency",
"fieldtype": "Link",
"hidden": 0,
@ -685,6 +705,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "paid_from",
"fetch_if_empty": 0,
"fieldname": "paid_from_account_balance",
"fieldtype": "Currency",
"hidden": 0,
@ -718,6 +739,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_18",
"fieldtype": "Column Break",
"hidden": 0,
@ -750,6 +772,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:(in_list([\"Internal Transfer\", \"Receive\"], doc.payment_type) || doc.party)",
"fetch_if_empty": 0,
"fieldname": "paid_to",
"fieldtype": "Link",
"hidden": 0,
@ -784,6 +807,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "paid_to",
"fetch_if_empty": 0,
"fieldname": "paid_to_account_currency",
"fieldtype": "Link",
"hidden": 0,
@ -818,6 +842,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "paid_to",
"fetch_if_empty": 0,
"fieldname": "paid_to_account_balance",
"fieldtype": "Currency",
"hidden": 0,
@ -853,6 +878,7 @@
"collapsible_depends_on": "",
"columns": 0,
"depends_on": "eval:(doc.paid_to && doc.paid_from)",
"fetch_if_empty": 0,
"fieldname": "payment_amounts_section",
"fieldtype": "Section Break",
"hidden": 0,
@ -886,6 +912,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "paid_amount",
"fieldtype": "Currency",
"hidden": 0,
@ -920,6 +947,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "source_exchange_rate",
"fieldtype": "Float",
"hidden": 0,
@ -953,6 +981,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "base_paid_amount",
"fieldtype": "Currency",
"hidden": 0,
@ -986,6 +1015,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_21",
"fieldtype": "Column Break",
"hidden": 0,
@ -1018,6 +1048,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "received_amount",
"fieldtype": "Currency",
"hidden": 0,
@ -1052,6 +1083,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "target_exchange_rate",
"fieldtype": "Float",
"hidden": 0,
@ -1085,6 +1117,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "base_received_amount",
"fieldtype": "Currency",
"hidden": 0,
@ -1120,6 +1153,7 @@
"collapsible_depends_on": "references",
"columns": 0,
"depends_on": "eval:(doc.party && doc.paid_from && doc.paid_to && doc.paid_amount && doc.received_amount)",
"fetch_if_empty": 0,
"fieldname": "section_break_14",
"fieldtype": "Section Break",
"hidden": 0,
@ -1154,6 +1188,7 @@
"columns": 0,
"default": "1",
"depends_on": "eval:in_list(['Pay', 'Receive'], doc.payment_type)",
"fetch_if_empty": 0,
"fieldname": "allocate_payment_amount",
"fieldtype": "Check",
"hidden": 0,
@ -1187,6 +1222,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "references",
"fieldtype": "Table",
"hidden": 0,
@ -1221,6 +1257,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "section_break_34",
"fieldtype": "Section Break",
"hidden": 0,
@ -1254,6 +1291,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:(doc.paid_amount && doc.received_amount && doc.references)",
"fetch_if_empty": 0,
"fieldname": "total_allocated_amount",
"fieldtype": "Currency",
"hidden": 0,
@ -1287,6 +1325,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "base_total_allocated_amount",
"fieldtype": "Currency",
"hidden": 0,
@ -1320,6 +1359,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "set_exchange_gain_loss",
"fieldtype": "Button",
"hidden": 0,
@ -1352,6 +1392,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_36",
"fieldtype": "Column Break",
"hidden": 0,
@ -1384,6 +1425,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:(doc.paid_amount && doc.received_amount && doc.references)",
"fetch_if_empty": 0,
"fieldname": "unallocated_amount",
"fieldtype": "Currency",
"hidden": 0,
@ -1417,6 +1459,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:(doc.paid_amount && doc.received_amount)",
"fetch_if_empty": 0,
"fieldname": "difference_amount",
"fieldtype": "Currency",
"hidden": 0,
@ -1451,6 +1494,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "difference_amount",
"fetch_if_empty": 0,
"fieldname": "write_off_difference_amount",
"fieldtype": "Button",
"hidden": 0,
@ -1485,6 +1529,7 @@
"collapsible_depends_on": "deductions",
"columns": 0,
"depends_on": "eval:(doc.paid_amount && doc.received_amount)",
"fetch_if_empty": 0,
"fieldname": "deductions_or_loss_section",
"fieldtype": "Section Break",
"hidden": 0,
@ -1517,6 +1562,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "deductions",
"fieldtype": "Table",
"hidden": 0,
@ -1550,6 +1596,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "transaction_references",
"fieldtype": "Section Break",
"hidden": 0,
@ -1583,6 +1630,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:(doc.paid_from && doc.paid_to)",
"fetch_if_empty": 0,
"fieldname": "reference_no",
"fieldtype": "Data",
"hidden": 0,
@ -1615,6 +1663,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_23",
"fieldtype": "Column Break",
"hidden": 0,
@ -1647,6 +1696,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:(doc.paid_from && doc.paid_to)",
"fetch_if_empty": 0,
"fieldname": "reference_date",
"fieldtype": "Date",
"hidden": 0,
@ -1680,6 +1730,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.docstatus==1",
"fetch_if_empty": 0,
"fieldname": "clearance_date",
"fieldtype": "Date",
"hidden": 0,
@ -1714,6 +1765,7 @@
"collapsible_depends_on": "",
"columns": 0,
"depends_on": "eval:(doc.paid_from && doc.paid_to && doc.paid_amount && doc.received_amount)",
"fetch_if_empty": 0,
"fieldname": "section_break_12",
"fieldtype": "Section Break",
"hidden": 0,
@ -1747,6 +1799,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "project",
"fieldtype": "Link",
"hidden": 0,
@ -1780,6 +1833,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "remarks",
"fieldtype": "Small Text",
"hidden": 0,
@ -1812,6 +1866,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_16",
"fieldtype": "Column Break",
"hidden": 0,
@ -1843,6 +1898,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "letter_head",
"fieldtype": "Link",
"hidden": 0,
@ -1876,6 +1932,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "print_heading",
"fieldtype": "Link",
"hidden": 0,
@ -1910,6 +1967,7 @@
"collapsible": 0,
"columns": 0,
"fetch_from": "bank_account.bank",
"fetch_if_empty": 0,
"fieldname": "bank",
"fieldtype": "Read Only",
"hidden": 0,
@ -1943,6 +2001,7 @@
"collapsible": 0,
"columns": 0,
"fetch_from": "bank_account.bank_account_no",
"fetch_if_empty": 0,
"fieldname": "bank_account_no",
"fieldtype": "Read Only",
"hidden": 0,
@ -1975,6 +2034,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "payment_order",
"fieldtype": "Link",
"hidden": 0,
@ -2008,6 +2068,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "subscription_section",
"fieldtype": "Section Break",
"hidden": 0,
@ -2040,6 +2101,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "auto_repeat",
"fieldtype": "Link",
"hidden": 0,
@ -2073,6 +2135,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 0,
@ -2105,6 +2168,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "title",
"fieldtype": "Data",
"hidden": 1,
@ -2141,7 +2205,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-01-15 15:58:40.742601",
"modified": "2019-03-27 17:39:54.163016",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",

View File

@ -14,6 +14,7 @@ from erpnext.accounts.general_ledger import make_gl_entries
from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
from erpnext.accounts.doctype.bank_account.bank_account import get_party_bank_account, get_bank_account_details
from erpnext.controllers.accounts_controller import AccountsController, get_supplier_block_status
from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import get_party_account_based_on_invoice_discounting
from six import string_types, iteritems
@ -237,7 +238,7 @@ class PaymentEntry(AccountsController):
if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Expense Claim", "Fees"):
if self.party_type == "Customer":
ref_party_account = ref_doc.debit_to
ref_party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or ref_doc.debit_to
elif self.party_type == "Student":
ref_party_account = ref_doc.receivable_account
elif self.party_type=="Supplier":
@ -536,9 +537,13 @@ class PaymentEntry(AccountsController):
@frappe.whitelist()
def get_outstanding_reference_documents(args):
if isinstance(args, string_types):
args = json.loads(args)
if args.get('party_type') == 'Member':
return
# confirm that Supplier is not blocked
if args.get('party_type') == 'Supplier':
supplier_status = get_supplier_block_status(args['party'])
@ -822,7 +827,7 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
# party account
if dt == "Sales Invoice":
party_account = doc.debit_to
party_account = get_party_account_based_on_invoice_discounting(dn) or doc.debit_to
elif dt == "Purchase Invoice":
party_account = doc.credit_to
elif dt == "Fees":

View File

@ -28,7 +28,7 @@ frappe.ui.form.on('POS Profile', {
return {
filters: [
['Print Format', 'doc_type', '=', 'Sales Invoice'],
['Print Format', 'print_format_type', '=', 'Server'],
['Print Format', 'print_format_type', '=', 'Jinja'],
]
};
});
@ -42,7 +42,7 @@ frappe.ui.form.on('POS Profile', {
});
frm.set_query("print_format", function() {
return { filters: { doc_type: "Sales Invoice", print_format_type: "Js"} };
return { filters: { doc_type: "Sales Invoice", print_format_type: "JS"} };
});
frappe.db.get_value('POS Settings', 'POS Settings', 'use_pos_in_offline_mode', (r) => {

View File

@ -96,7 +96,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
},
get_query_filters: {
docstatus: 1,
status: ["!=", "Closed"],
status: ["not in", ["Closed", "On Hold"]],
per_billed: ["<", 99.99],
company: me.frm.doc.company
}

View File

@ -16,7 +16,7 @@ from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_bille
from erpnext.stock import get_warehouse_account_map
from erpnext.accounts.general_ledger import make_gl_entries, merge_similar_entries, delete_gl_entries
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
from erpnext.buying.utils import check_for_closed_status
from erpnext.buying.utils import check_on_hold_or_closed_status
from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center
from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accounting_disabled
from frappe.model.mapper import get_mapped_doc
@ -89,7 +89,7 @@ class PurchaseInvoice(BuyingController):
self.check_conversion_rate()
self.validate_credit_to_acc()
self.clear_unallocated_advances("Purchase Invoice Advance", "advances")
self.check_for_closed_status()
self.check_on_hold_or_closed_status()
self.validate_with_previous_doc()
self.validate_uom_is_integer("uom", "qty")
self.validate_uom_is_integer("stock_uom", "stock_qty")
@ -152,13 +152,13 @@ class PurchaseInvoice(BuyingController):
self.party_account_currency = account.account_currency
def check_for_closed_status(self):
def check_on_hold_or_closed_status(self):
check_list = []
for d in self.get('items'):
if d.purchase_order and not d.purchase_order in check_list and not d.purchase_receipt:
check_list.append(d.purchase_order)
check_for_closed_status('Purchase Order', d.purchase_order)
check_on_hold_or_closed_status('Purchase Order', d.purchase_order)
def validate_with_previous_doc(self):
super(PurchaseInvoice, self).validate_with_previous_doc({
@ -760,7 +760,7 @@ class PurchaseInvoice(BuyingController):
def on_cancel(self):
super(PurchaseInvoice, self).on_cancel()
self.check_for_closed_status()
self.check_on_hold_or_closed_status()
self.update_status_updater_args()

View File

@ -344,6 +344,7 @@ class TestPurchaseInvoice(unittest.TestCase):
pi = frappe.copy_doc(test_records[0])
pi.disable_rounded_total = 1
pi.allocate_advances_automatically = 0
pi.append("advances", {
"reference_type": "Journal Entry",
"reference_name": jv.name,
@ -383,6 +384,7 @@ class TestPurchaseInvoice(unittest.TestCase):
pi = frappe.copy_doc(test_records[0])
pi.disable_rounded_total = 1
pi.allocate_advances_automatically = 0
pi.append("advances", {
"reference_type": "Journal Entry",
"reference_name": jv.name,

View File

@ -171,7 +171,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
},
get_query_filters: {
docstatus: 1,
status: ["!=", "Closed"],
status: ["not in", ["Closed", "On Hold"]],
per_billed: ["<", 99.99],
company: me.frm.doc.company
}

File diff suppressed because it is too large Load Diff

View File

@ -81,7 +81,7 @@ class SalesInvoice(SellingController):
self.validate_with_previous_doc()
self.validate_uom_is_integer("stock_uom", "stock_qty")
self.validate_uom_is_integer("uom", "qty")
self.check_close_sales_order("sales_order")
self.check_sales_order_on_hold_or_close("sales_order")
self.validate_debit_to_acc()
self.clear_unallocated_advances("Sales Invoice Advance", "advances")
self.add_remarks()
@ -209,7 +209,7 @@ class SalesInvoice(SellingController):
def on_cancel(self):
super(SalesInvoice, self).on_cancel()
self.check_close_sales_order("sales_order")
self.check_sales_order_on_hold_or_close("sales_order")
if self.is_return and not self.update_billed_amount_in_sales_order:
# NOTE status updating bypassed for is_return

View File

@ -18,7 +18,7 @@ def get_data():
'transactions': [
{
'label': _('Payment'),
'items': ['Payment Entry', 'Payment Request', 'Journal Entry']
'items': ['Payment Entry', 'Payment Request', 'Journal Entry', 'Invoice Discounting']
},
{
'label': _('Reference'),

View File

@ -913,6 +913,7 @@ class TestSalesInvoice(unittest.TestCase):
jv.submit()
si = frappe.copy_doc(test_records[0])
si.allocate_advances_automatically = 0
si.append("advances", {
"doctype": "Sales Invoice Advance",
"reference_type": "Journal Entry",

View File

@ -107,11 +107,14 @@
<thead>
<tr>
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
<th style="width: 7%">{%= __("Date") %}</th>
<th style="width: 7%">{%= __("Age (Days)") %}</th>
<th style="width: 13%">{%= __("Reference") %}</th>
{% if(report.report_name === "Accounts Receivable") { %}
<th style="width: 10%">{%= __("Date") %}</th>
<th style="width: 4%">{%= __("Age (Days)") %}</th>
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
<th style="width: 16%">{%= __("Reference") %}</th>
<th style="width: 10%">{%= __("Sales Person") %}</th>
{% } else { %}
<th style="width: 26%">{%= __("Reference") %}</th>
{% } %}
{% if(!filters.show_pdc_in_print) { %}
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
@ -121,12 +124,11 @@
<th style="width: 10%; text-align: right">{%= __("Paid Amount") %}</th>
<th style="width: 10%; text-align: right">{%= report.report_name === "Accounts Receivable" ? __('Credit Note') : __('Debit Note') %}</th>
{% } %}
<th style="width: 15%; text-align: right">{%= __("Outstanding Amount") %}</th>
<th style="width: 10%; text-align: right">{%= __("Outstanding Amount") %}</th>
{% if(filters.show_pdc_in_print) { %}
{% if(report.report_name === "Accounts Receivable") { %}
<th style="width: 10%">{%= __("Customer LPO No.") %}</th>
{% } %}
<th style="width: 10%">{%= __("PDC/LC Date") %}</th>
<th style="width: 10%">{%= __("PDC/LC Ref") %}</th>
<th style="width: 10%">{%= __("PDC/LC Amount") %}</th>
<th style="width: 10%">{%= __("Remaining Balance") %}</th>
@ -155,7 +157,7 @@
{%= data[i]["voucher_no"] %}
</td>
{% if(report.report_name === "Accounts Receivable") { %}
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
<td>{%= data[i]["sales_person"] %}</td>
{% } %}
@ -195,7 +197,6 @@
<td style="text-align: right">
{%= data[i]["po_no"] %}</td>
{% } %}
<td style="text-align: right">{%= frappe.datetime.str_to_user(data[i][("pdc/lc_date")]) %}</td>
<td style="text-align: right">{%= data[i][("pdc/lc_ref")] %}</td>
<td style="text-align: right">{%= format_currency(data[i][("pdc/lc_amount")], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i][("remaining_balance")], data[i]["currency"]) %}</td>
@ -205,7 +206,7 @@
{% if(!filters.show_pdc_in_print) { %}
<td></td>
{% } %}
{% if(report.report_name === "Accounts Receivable") { %}
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
<td></td>
{% } %}
<td></td>
@ -226,7 +227,6 @@
<td style="text-align: right">
{%= data[i][__("Customer LPO")] %}</td>
{% } %}
<td style="text-align: right">{%= frappe.datetime.str_to_user(data[i][__("PDC/LC Date")]) %}</td>
<td style="text-align: right">{%= data[i][("pdc/lc_ref")] %}</td>
<td style="text-align: right">{%= format_currency(data[i][("pdc/lc_amount")], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i][("remaining_balance")], data[i]["currency"]) %}</td>

View File

@ -116,14 +116,19 @@ frappe.query_reports["Accounts Receivable"] = {
"fieldtype": "Link",
"options": "Sales Person"
},
{
"fieldname":"based_on_payment_terms",
"label": __("Based On Payment Terms"),
"fieldtype": "Check",
},
{
"fieldname":"show_pdc_in_print",
"label": __("Show PDC in Print"),
"fieldtype": "Check",
},
{
"fieldname":"based_on_payment_terms",
"label": __("Based On Payment Terms"),
"fieldname":"show_sales_person_in_print",
"label": __("Show Sales Person in Print"),
"fieldtype": "Check",
},
{

View File

@ -481,13 +481,8 @@ class ReceivablePayableReport(object):
conditions.append("company=%s")
values.append(self.filters.company)
company_finance_book = erpnext.get_default_finance_book(self.filters.company)
if not self.filters.finance_book or (self.filters.finance_book == company_finance_book):
if self.filters.finance_book:
conditions.append("ifnull(finance_book,'') in (%s, '')")
values.append(company_finance_book)
elif self.filters.finance_book:
conditions.append("ifnull(finance_book,'') = %s")
values.append(self.filters.finance_book)
if self.filters.get(party_type_field):

View File

@ -31,11 +31,8 @@ def get_data(filters):
filters_data.append(["against_voucher", "in", assets])
company_finance_book = erpnext.get_default_finance_book(filters.get("company"))
if (not filters.get('finance_book') or (filters.get('finance_book') == company_finance_book)):
if filters.get("finance_book"):
filters_data.append(["finance_book", "in", ['', filters.get('finance_book')]])
elif filters.get("finance_book"):
filters_data.append(["finance_book", "=", filters.get('finance_book')])
gl_entries = frappe.get_all('GL Entry',
filters= filters_data,

View File

@ -355,7 +355,8 @@ def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, g
"to_date": to_date,
"lft": root_lft,
"rgt": root_rgt,
"company": d.name
"company": d.name,
"finance_book": filters.get("finance_book")
},
as_dict=True)
@ -385,14 +386,8 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters):
if from_date:
additional_conditions.append("gl.posting_date >= %(from_date)s")
company_finance_book = erpnext.get_default_finance_book(filters.get("company"))
if not filters.get('finance_book') or (filters.get('finance_book') == company_finance_book):
additional_conditions.append("ifnull(finance_book, '') in (%s, '')" %
frappe.db.escape(company_finance_book))
elif filters.get("finance_book"):
additional_conditions.append("ifnull(finance_book, '') = %s " %
frappe.db.escape(filters.get("finance_book")))
if filters.get("finance_book"):
additional_conditions.append("ifnull(finance_book, '') in (%(finance_book)s, '')")
return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else ""

View File

@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe
import erpnext
from frappe import _
from frappe import _, scrub
from frappe.utils import getdate, nowdate
from six import iteritems, itervalues
@ -14,6 +14,9 @@ class PartyLedgerSummaryReport(object):
self.filters.from_date = getdate(self.filters.from_date or nowdate())
self.filters.to_date = getdate(self.filters.to_date or nowdate())
if not self.filters.get("company"):
self.filters["company"] = frappe.db.get_single_value('Global Defaults', 'default_company')
def run(self, args):
if self.filters.from_date > self.filters.to_date:
frappe.throw(_("From Date must be before To Date"))
@ -21,10 +24,9 @@ class PartyLedgerSummaryReport(object):
self.filters.party_type = args.get("party_type")
self.party_naming_by = frappe.db.get_value(args.get("naming_by")[0], None, args.get("naming_by")[1])
discount_account_field = "discount_allowed_account" if self.filters.party_type == "Customer" \
else "discount_received_account"
self.round_off_account, self.write_off_account, self.discount_account = frappe.get_cached_value('Company',
self.filters.company, ["round_off_account", "write_off_account", discount_account_field])
self.get_gl_entries()
self.get_return_invoices()
self.get_party_adjustment_amounts()
columns = self.get_columns()
data = self.get_data()
@ -48,7 +50,6 @@ class PartyLedgerSummaryReport(object):
})
credit_or_debit_note = "Credit Note" if self.filters.party_type == "Customer" else "Debit Note"
discount_allowed_or_received = "Discount Allowed" if self.filters.party_type == "Customer" else "Discount Received"
columns += [
{
@ -79,27 +80,19 @@ class PartyLedgerSummaryReport(object):
"options": "currency",
"width": 120
},
{
"label": _(discount_allowed_or_received),
"fieldname": "discount_amount",
]
for account in self.party_adjustment_accounts:
columns.append({
"label": account,
"fieldname": "adj_" + scrub(account),
"fieldtype": "Currency",
"options": "currency",
"width": 120
},
{
"label": _("Write Off Amount"),
"fieldname": "write_off_amount",
"fieldtype": "Currency",
"options": "currency",
"width": 120
},
{
"label": _("Other Adjustments"),
"fieldname": "adjustment_amount",
"fieldtype": "Currency",
"options": "currency",
"width": 120
},
"width": 120,
"is_adjustment": 1
})
columns += [
{
"label": _("Closing Balance"),
"fieldname": "closing_balance",
@ -119,17 +112,10 @@ class PartyLedgerSummaryReport(object):
return columns
def get_data(self):
if not self.filters.get("company"):
self.filters["company"] = frappe.db.get_single_value('Global Defaults', 'default_company')
company_currency = frappe.get_cached_value('Company', self.filters.get("company"), "default_currency")
invoice_dr_or_cr = "debit" if self.filters.party_type == "Customer" else "credit"
reverse_dr_or_cr = "credit" if self.filters.party_type == "Customer" else "debit"
self.get_gl_entries()
self.get_return_invoices()
self.get_party_adjustment_amounts()
self.party_data = frappe._dict({})
for gle in self.gl_entries:
self.party_data.setdefault(gle.party, frappe._dict({
@ -146,7 +132,7 @@ class PartyLedgerSummaryReport(object):
amount = gle.get(invoice_dr_or_cr) - gle.get(reverse_dr_or_cr)
self.party_data[gle.party].closing_balance += amount
if gle.posting_date < self.filters.from_date:
if gle.posting_date < self.filters.from_date or gle.is_opening == "Yes":
self.party_data[gle.party].opening_balance += amount
else:
if amount > 0:
@ -161,9 +147,10 @@ class PartyLedgerSummaryReport(object):
if row.opening_balance or row.invoiced_amount or row.paid_amount or row.return_amount or row.closing_amount:
total_party_adjustment = sum([amount for amount in itervalues(self.party_adjustment_details.get(party, {}))])
row.paid_amount -= total_party_adjustment
row.discount_amount = self.party_adjustment_details.get(party, {}).get(self.discount_account, 0)
row.write_off_amount = self.party_adjustment_details.get(party, {}).get(self.write_off_account, 0)
row.adjustment_amount = total_party_adjustment - row.discount_amount - row.write_off_amount
adjustments = self.party_adjustment_details.get(party, {})
for account in self.party_adjustment_accounts:
row["adj_" + scrub(account)] = adjustments.get(account, 0)
out.append(row)
@ -182,7 +169,7 @@ class PartyLedgerSummaryReport(object):
self.gl_entries = frappe.db.sql("""
select
gle.posting_date, gle.party, gle.voucher_type, gle.voucher_no, gle.against_voucher_type,
gle.against_voucher, gle.debit, gle.credit {join_field}
gle.against_voucher, gle.debit, gle.credit, gle.is_opening {join_field}
from `tabGL Entry` gle
{join}
where
@ -197,12 +184,8 @@ class PartyLedgerSummaryReport(object):
if self.filters.company:
conditions.append("gle.company=%(company)s")
self.filters.company_finance_book = erpnext.get_default_finance_book(self.filters.company)
if not self.filters.finance_book or (self.filters.finance_book == self.filters.company_finance_book):
conditions.append("ifnull(finance_book,'') in (%(company_finance_book)s, '')")
elif self.filters.finance_book:
conditions.append("ifnull(finance_book,'') = %(finance_book)s")
if self.filters.finance_book:
conditions.append("ifnull(finance_book,'') in (%(finance_book)s, '')")
if self.filters.get("party"):
conditions.append("party=%(party)s")
@ -254,9 +237,10 @@ class PartyLedgerSummaryReport(object):
def get_party_adjustment_amounts(self):
conditions = self.prepare_conditions()
income_or_expense = "Expense" if self.filters.party_type == "Customer" else "Income"
income_or_expense = "Expense Account" if self.filters.party_type == "Customer" else "Income Account"
invoice_dr_or_cr = "debit" if self.filters.party_type == "Customer" else "credit"
reverse_dr_or_cr = "credit" if self.filters.party_type == "Customer" else "debit"
round_off_account = frappe.get_cached_value('Company', self.filters.company, "round_off_account")
gl_entries = frappe.db.sql("""
select
@ -267,7 +251,7 @@ class PartyLedgerSummaryReport(object):
docstatus < 2
and (voucher_type, voucher_no) in (
select voucher_type, voucher_no from `tabGL Entry` gle, `tabAccount` acc
where acc.name = gle.account and acc.root_type = '{income_or_expense}'
where acc.name = gle.account and acc.account_type = '{income_or_expense}'
and gle.posting_date between %(from_date)s and %(to_date)s and gle.docstatus < 2
) and (voucher_type, voucher_no) in (
select voucher_type, voucher_no from `tabGL Entry` gle
@ -277,6 +261,7 @@ class PartyLedgerSummaryReport(object):
""".format(conditions=conditions, income_or_expense=income_or_expense), self.filters, as_dict=True)
self.party_adjustment_details = {}
self.party_adjustment_accounts = set()
adjustment_voucher_entries = {}
for gle in gl_entries:
adjustment_voucher_entries.setdefault((gle.voucher_type, gle.voucher_no), [])
@ -288,12 +273,12 @@ class PartyLedgerSummaryReport(object):
has_irrelevant_entry = False
for gle in voucher_gl_entries:
if gle.account == self.round_off_account:
if gle.account == round_off_account:
continue
elif gle.party:
parties.setdefault(gle.party, 0)
parties[gle.party] += gle.get(reverse_dr_or_cr) - gle.get(invoice_dr_or_cr)
elif frappe.get_cached_value("Account", gle.account, "root_type") == income_or_expense:
elif frappe.get_cached_value("Account", gle.account, "account_type") == income_or_expense:
accounts.setdefault(gle.account, 0)
accounts[gle.account] += gle.get(invoice_dr_or_cr) - gle.get(reverse_dr_or_cr)
else:
@ -303,11 +288,13 @@ class PartyLedgerSummaryReport(object):
if len(parties) == 1:
party = parties.keys()[0]
for account, amount in iteritems(accounts):
self.party_adjustment_accounts.add(account)
self.party_adjustment_details.setdefault(party, {})
self.party_adjustment_details[party].setdefault(account, 0)
self.party_adjustment_details[party][account] += amount
elif len(accounts) == 1 and not has_irrelevant_entry:
account = accounts.keys()[0]
self.party_adjustment_accounts.add(account)
for party, amount in iteritems(parties):
self.party_adjustment_details.setdefault(party, {})
self.party_adjustment_details[party].setdefault(account, 0)

View File

@ -392,14 +392,8 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters):
filters.cost_center = get_cost_centers_with_children(filters.cost_center)
additional_conditions.append("cost_center in %(cost_center)s")
company_finance_book = erpnext.get_default_finance_book(filters.get("company"))
if not filters.get('finance_book') or (filters.get('finance_book') == company_finance_book):
additional_conditions.append("ifnull(finance_book, '') in (%s, '')" %
frappe.db.escape(company_finance_book))
elif filters.get("finance_book"):
additional_conditions.append("ifnull(finance_book, '') = %s " %
frappe.db.escape(filters.get("finance_book")))
if filters.get("finance_book"):
additional_conditions.append("ifnull(finance_book, '') in (%(finance_book)s, '')")
return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else ""

View File

@ -186,12 +186,8 @@ def get_conditions(filters):
if filters.get("project"):
conditions.append("project in %(project)s")
company_finance_book = erpnext.get_default_finance_book(filters.get("company"))
if not filters.get("finance_book") or (filters.get("finance_book") == company_finance_book):
filters['finance_book'] = company_finance_book
if filters.get("finance_book"):
conditions.append("ifnull(finance_book, '') in (%(finance_book)s, '')")
elif filters.get("finance_book"):
conditions.append("ifnull(finance_book, '') = %(finance_book)s")
from frappe.desk.reportview import build_match_conditions
match_conditions = build_match_conditions("GL Entry")

View File

@ -54,8 +54,10 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
delivery_note, d.income_account, d.cost_center, d.stock_qty, d.stock_uom
]
row += [(d.base_net_rate * d.qty)/d.stock_qty, d.base_net_amount] \
if d.stock_uom != d.uom and d.stock_qty != 0 else [d.base_net_rate, d.base_net_amount]
if d.stock_uom != d.uom and d.stock_qty:
row += [(d.base_net_rate * d.qty)/d.stock_qty, d.base_net_amount]
else:
row += [d.base_net_rate, d.base_net_amount]
total_tax = 0
for tax in tax_columns:

View File

@ -1,23 +1,27 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2017-12-27 16:15:52.615453",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"modified": "2017-12-27 16:46:54.422356",
"modified": "2019-04-19 10:50:36.061588",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Share Ledger",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Share Transfer",
"report_name": "Share Ledger",
"report_type": "Script Report",
"roles": [
{
"role": "Administrator"
},
{
"role": "System Manager"
}
]
}

View File

@ -55,12 +55,15 @@ def get_result(filters):
supplier = supplier_map[d]
tds_doc = tds_docs[supplier.tax_withholding_category]
account = [i.account for i in tds_doc.accounts if i.company == filters.company][0]
account_list = [i.account for i in tds_doc.accounts if i.company == filters.company]
if account_list:
account = account_list[0]
for k in gle_map[d]:
if k.party == supplier_map[d] and k.credit > 0:
total_amount_credited += k.credit
elif k.account == account and k.credit > 0:
elif account_list and k.account == account and k.credit > 0:
tds_deducted = k.credit
total_amount_credited += k.credit

View File

@ -112,13 +112,15 @@ def convert_to_presentation_currency(gl_entries, currency_info):
if entry.get('debit'):
entry['debit'] = converted_value
else:
if entry.get('credit'):
entry['credit'] = converted_value
elif account_currency == presentation_currency:
if entry.get('debit'):
entry['debit'] = debit_in_account_currency
else:
if entry.get('credit'):
entry['credit'] = credit_in_account_currency
converted_gl_list.append(entry)

View File

@ -206,12 +206,10 @@ frappe.ui.form.on('Asset', {
erpnext.asset.set_accululated_depreciation(frm);
},
depreciation_method: function(frm) {
frm.events.make_schedules_editable(frm);
},
make_schedules_editable: function(frm) {
var is_editable = frm.doc.depreciation_method==="Manual" ? true : false;
var is_editable = frm.doc.finance_books.filter(d => d.depreciation_method == "Manual").length > 0
? true : false;
frm.toggle_enable("schedules", is_editable);
frm.fields_dict["schedules"].grid.toggle_enable("schedule_date", is_editable);
frm.fields_dict["schedules"].grid.toggle_enable("depreciation_amount", is_editable);
@ -296,6 +294,44 @@ frappe.ui.form.on('Asset', {
})
frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation);
},
set_depreciation_rate: function(frm, row) {
if (row.total_number_of_depreciations && row.frequency_of_depreciation) {
frappe.call({
method: "get_depreciation_rate",
doc: frm.doc,
args: row,
callback: function(r) {
if (r.message) {
frappe.model.set_value(row.doctype, row.name, "rate_of_depreciation", r.message);
}
}
});
}
}
});
frappe.ui.form.on('Asset Finance Book', {
depreciation_method: function(frm, cdt, cdn) {
const row = locals[cdt][cdn];
frm.events.set_depreciation_rate(frm, row);
frm.events.make_schedules_editable(frm);
},
expected_value_after_useful_life: function(frm, cdt, cdn) {
const row = locals[cdt][cdn];
frm.events.set_depreciation_rate(frm, row);
},
frequency_of_depreciation: function(frm, cdt, cdn) {
const row = locals[cdt][cdn];
frm.events.set_depreciation_rate(frm, row);
},
total_number_of_depreciations: function(frm, cdt, cdn) {
const row = locals[cdt][cdn];
frm.events.set_depreciation_rate(frm, row);
}
});

View File

@ -21,6 +21,7 @@
"collapsible": 0,
"columns": 0,
"default": "",
"fetch_if_empty": 0,
"fieldname": "naming_series",
"fieldtype": "Select",
"hidden": 0,
@ -54,6 +55,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "asset_name",
"fieldtype": "Data",
"hidden": 0,
@ -86,6 +88,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "item_code",
"fieldtype": "Link",
"hidden": 0,
@ -120,6 +123,7 @@
"collapsible": 0,
"columns": 0,
"fetch_from": "item_code.item_name",
"fetch_if_empty": 0,
"fieldname": "item_name",
"fieldtype": "Read Only",
"hidden": 0,
@ -154,6 +158,7 @@
"collapsible": 0,
"columns": 0,
"fetch_from": "item_code.asset_category",
"fetch_if_empty": 0,
"fieldname": "asset_category",
"fieldtype": "Link",
"hidden": 0,
@ -187,6 +192,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "asset_owner",
"fieldtype": "Select",
"hidden": 0,
@ -221,6 +227,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.asset_owner == \"Company\"",
"fetch_if_empty": 0,
"fieldname": "asset_owner_company",
"fieldtype": "Link",
"hidden": 0,
@ -255,6 +262,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.asset_owner == \"Supplier\"",
"fetch_if_empty": 0,
"fieldname": "supplier",
"fieldtype": "Link",
"hidden": 0,
@ -289,6 +297,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.asset_owner == \"Customer\"",
"fetch_if_empty": 0,
"fieldname": "customer",
"fieldtype": "Link",
"hidden": 0,
@ -322,6 +331,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "image",
"fieldtype": "Attach Image",
"hidden": 1,
@ -354,6 +364,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
@ -385,6 +396,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
@ -418,6 +430,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "location",
"fieldtype": "Link",
"hidden": 0,
@ -451,10 +464,11 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "custodian",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
@ -484,6 +498,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "cost_center",
"fieldtype": "Link",
"hidden": 0,
@ -517,6 +532,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "department",
"fieldtype": "Link",
"hidden": 0,
@ -550,6 +566,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "purchase_date",
"fieldtype": "Date",
"hidden": 0,
@ -582,6 +599,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "disposal_date",
"fieldtype": "Date",
"hidden": 0,
@ -614,6 +632,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "journal_entry_for_scrap",
"fieldtype": "Link",
"hidden": 0,
@ -647,6 +666,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "section_break_5",
"fieldtype": "Section Break",
"hidden": 0,
@ -678,6 +698,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "gross_purchase_amount",
"fieldtype": "Currency",
"hidden": 0,
@ -711,6 +732,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "available_for_use_date",
"fieldtype": "Date",
"hidden": 0,
@ -743,6 +765,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_18",
"fieldtype": "Column Break",
"hidden": 0,
@ -774,6 +797,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "calculate_depreciation",
"fieldtype": "Check",
"hidden": 0,
@ -806,6 +830,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "is_existing_asset",
"fieldtype": "Check",
"hidden": 0,
@ -839,6 +864,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "is_existing_asset",
"fetch_if_empty": 0,
"fieldname": "opening_accumulated_depreciation",
"fieldtype": "Currency",
"hidden": 0,
@ -873,6 +899,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:(doc.is_existing_asset && doc.opening_accumulated_depreciation)",
"fetch_if_empty": 0,
"fieldname": "number_of_depreciations_booked",
"fieldtype": "Int",
"hidden": 0,
@ -906,6 +933,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "calculate_depreciation",
"fetch_if_empty": 0,
"fieldname": "section_break_23",
"fieldtype": "Section Break",
"hidden": 0,
@ -938,6 +966,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "finance_books",
"fieldtype": "Table",
"hidden": 0,
@ -971,6 +1000,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "section_break_33",
"fieldtype": "Section Break",
"hidden": 1,
@ -1004,6 +1034,7 @@
"columns": 0,
"default": "",
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "depreciation_method",
"fieldtype": "Select",
"hidden": 0,
@ -1037,6 +1068,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "value_after_depreciation",
"fieldtype": "Currency",
"hidden": 1,
@ -1070,6 +1102,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "total_number_of_depreciations",
"fieldtype": "Int",
"hidden": 0,
@ -1102,6 +1135,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_24",
"fieldtype": "Column Break",
"hidden": 0,
@ -1133,6 +1167,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "frequency_of_depreciation",
"fieldtype": "Int",
"hidden": 0,
@ -1166,6 +1201,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "next_depreciation_date",
"fieldtype": "Date",
"hidden": 0,
@ -1199,6 +1235,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "calculate_depreciation",
"fetch_if_empty": 0,
"fieldname": "section_break_14",
"fieldtype": "Section Break",
"hidden": 0,
@ -1231,6 +1268,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "schedules",
"fieldtype": "Table",
"hidden": 0,
@ -1264,6 +1302,7 @@
"bold": 0,
"collapsible": 1,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "insurance_details",
"fieldtype": "Section Break",
"hidden": 0,
@ -1296,6 +1335,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "policy_number",
"fieldtype": "Data",
"hidden": 0,
@ -1328,6 +1368,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "insurer",
"fieldtype": "Data",
"hidden": 0,
@ -1360,6 +1401,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "insured_value",
"fieldtype": "Data",
"hidden": 0,
@ -1392,6 +1434,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_48",
"fieldtype": "Column Break",
"hidden": 0,
@ -1423,6 +1466,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "insurance_start_date",
"fieldtype": "Date",
"hidden": 0,
@ -1455,6 +1499,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "insurance_end_date",
"fieldtype": "Date",
"hidden": 0,
@ -1487,6 +1532,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "comprehensive_insurance",
"fieldtype": "Data",
"hidden": 0,
@ -1519,6 +1565,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "section_break_31",
"fieldtype": "Section Break",
"hidden": 0,
@ -1552,6 +1599,7 @@
"collapsible": 0,
"columns": 0,
"description": "Check if Asset requires Preventive Maintenance or Calibration",
"fetch_if_empty": 0,
"fieldname": "maintenance_required",
"fieldtype": "Check",
"hidden": 0,
@ -1584,6 +1632,7 @@
"bold": 0,
"collapsible": 1,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "other_details",
"fieldtype": "Section Break",
"hidden": 0,
@ -1617,6 +1666,7 @@
"collapsible": 0,
"columns": 0,
"default": "Draft",
"fetch_if_empty": 0,
"fieldname": "status",
"fieldtype": "Select",
"hidden": 0,
@ -1651,6 +1701,7 @@
"collapsible": 0,
"columns": 0,
"default": "0",
"fetch_if_empty": 0,
"fieldname": "booked_fixed_asset",
"fieldtype": "Check",
"hidden": 0,
@ -1683,6 +1734,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_51",
"fieldtype": "Column Break",
"hidden": 0,
@ -1714,6 +1766,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "purchase_receipt",
"fieldtype": "Link",
"hidden": 0,
@ -1747,6 +1800,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "purchase_receipt_amount",
"fieldtype": "Currency",
"hidden": 1,
@ -1779,6 +1833,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "purchase_invoice",
"fieldtype": "Link",
"hidden": 0,
@ -1813,6 +1868,7 @@
"collapsible": 0,
"columns": 0,
"fetch_from": "company.default_finance_book",
"fetch_if_empty": 0,
"fieldname": "default_finance_book",
"fieldtype": "Link",
"hidden": 1,
@ -1846,6 +1902,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 0,
@ -1873,17 +1930,16 @@
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 72,
"image_field": "image",
"image_view": 0,
"in_create": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-02-12 11:29:01.747819",
"menu_index": 0,
"modified": "2019-04-15 18:18:48.492675",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset",
@ -1931,7 +1987,6 @@
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 1,
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",

View File

@ -3,8 +3,9 @@
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe, erpnext
import frappe, erpnext, math, json
from frappe import _
from six import string_types
from frappe.utils import flt, add_months, cint, nowdate, getdate, today, date_diff
from frappe.model.document import Document
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
@ -20,6 +21,7 @@ class Asset(AccountsController):
self.validate_item()
self.set_missing_values()
if self.calculate_depreciation:
self.set_depreciation_rate()
self.make_depreciation_schedule()
self.set_accumulated_depreciation()
else:
@ -89,17 +91,22 @@ class Asset(AccountsController):
if self.is_existing_asset:
return
date = nowdate()
docname = self.purchase_receipt or self.purchase_invoice
if docname:
doctype = 'Purchase Receipt' if self.purchase_receipt else 'Purchase Invoice'
date = frappe.db.get_value(doctype, docname, 'posting_date')
if self.available_for_use_date and getdate(self.available_for_use_date) < getdate(date):
if self.available_for_use_date and getdate(self.available_for_use_date) < getdate(self.purchase_date):
frappe.throw(_("Available-for-use Date should be after purchase date"))
def set_depreciation_rate(self):
for d in self.get("finance_books"):
d.rate_of_depreciation = self.get_depreciation_rate(d)
def make_depreciation_schedule(self):
if self.depreciation_method != 'Manual':
depreciation_method = [d.depreciation_method for d in self.finance_books]
if 'Manual' not in depreciation_method:
self.schedules = []
if not self.get("schedules") and self.available_for_use_date:
@ -254,11 +261,13 @@ class Asset(AccountsController):
return flt(self.get('finance_books')[cint(idx)-1].value_after_depreciation)
def get_depreciation_amount(self, depreciable_value, total_number_of_depreciations, row):
percentage_value = 100.0 if row.depreciation_method == 'Written Down Value' else 200.0
factor = percentage_value / cint(total_number_of_depreciations)
depreciation_amount = flt(depreciable_value * factor / 100, 0)
if row.depreciation_method in ["Straight Line", "Manual"]:
amt = (flt(self.gross_purchase_amount) - flt(row.expected_value_after_useful_life) -
flt(self.opening_accumulated_depreciation))
depreciation_amount = amt * row.rate_of_depreciation
else:
depreciation_amount = flt(depreciable_value) * (flt(row.rate_of_depreciation) / 100)
value_after_depreciation = flt(depreciable_value) - depreciation_amount
if value_after_depreciation < flt(row.expected_value_after_useful_life):
depreciation_amount = flt(depreciable_value) - flt(row.expected_value_after_useful_life)
@ -394,6 +403,32 @@ class Asset(AccountsController):
make_gl_entries(gl_entries)
self.db_set('booked_fixed_asset', 1)
def get_depreciation_rate(self, args):
if isinstance(args, string_types):
args = json.loads(args)
number_of_depreciations_booked = 0
if self.is_existing_asset:
number_of_depreciations_booked = self.number_of_depreciations_booked
float_precision = cint(frappe.db.get_default("float_precision")) or 2
tot_no_of_depreciation = flt(args.get("total_number_of_depreciations")) - flt(number_of_depreciations_booked)
if args.get("depreciation_method") in ["Straight Line", "Manual"]:
return 1.0 / tot_no_of_depreciation
if args.get("depreciation_method") == 'Double Declining Balance':
return 200.0 / args.get("total_number_of_depreciations")
if args.get("depreciation_method") == "Written Down Value" and not args.get("rate_of_depreciation"):
no_of_years = flt(args.get("total_number_of_depreciations") * flt(args.get("frequency_of_depreciation"))) / 12
value = flt(args.get("expected_value_after_useful_life")) / flt(self.gross_purchase_amount)
# square root of flt(salvage_value) / flt(asset_cost)
depreciation_rate = math.pow(value, 1.0/flt(no_of_years, 2))
return 100 * (1 - flt(depreciation_rate, float_precision))
def update_maintenance_status():
assets = frappe.get_all('Asset', filters = {'docstatus': 1, 'maintenance_required': 1})
@ -480,7 +515,6 @@ def create_asset_adjustment(asset, asset_category, company):
@frappe.whitelist()
def transfer_asset(args):
import json
args = json.loads(args)
if args.get('serial_no'):

View File

@ -160,9 +160,9 @@ class TestAsset(unittest.TestCase):
asset.save()
expected_schedules = [
["2020-06-06", 66667.0, 66667.0],
["2021-04-06", 22222.0, 88889.0],
["2022-02-06", 1111.0, 90000.0]
["2020-06-06", 66666.67, 66666.67],
["2021-04-06", 22222.22, 88888.89],
["2022-02-06", 1111.11, 90000.0]
]
schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
@ -192,8 +192,8 @@ class TestAsset(unittest.TestCase):
asset.save()
expected_schedules = [
["2020-06-06", 33333.0, 83333.0],
["2021-04-06", 6667.0, 90000.0]
["2020-06-06", 33333.33, 83333.33],
["2021-04-06", 6666.67, 90000.0]
]
schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
@ -209,7 +209,7 @@ class TestAsset(unittest.TestCase):
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
asset = frappe.get_doc('Asset', asset_name)
asset.calculate_depreciation = 1
asset.purchase_date = '2020-06-06'
asset.purchase_date = '2020-01-30'
asset.is_existing_asset = 0
asset.available_for_use_date = "2020-01-30"
asset.append("finance_books", {
@ -244,7 +244,7 @@ class TestAsset(unittest.TestCase):
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
asset = frappe.get_doc('Asset', asset_name)
asset.calculate_depreciation = 1
asset.purchase_date = '2020-06-06'
asset.purchase_date = '2020-01-30'
asset.available_for_use_date = "2020-01-30"
asset.append("finance_books", {
"expected_value_after_useful_life": 10000,
@ -277,6 +277,37 @@ class TestAsset(unittest.TestCase):
self.assertEqual(gle, expected_gle)
self.assertEqual(asset.get("value_after_depreciation"), 0)
def test_depreciation_entry_for_wdv(self):
pr = make_purchase_receipt(item_code="Macbook Pro",
qty=1, rate=8000.0, location="Test Location")
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
asset = frappe.get_doc('Asset', asset_name)
asset.calculate_depreciation = 1
asset.available_for_use_date = '2030-06-06'
asset.purchase_date = '2030-06-06'
asset.append("finance_books", {
"expected_value_after_useful_life": 1000,
"depreciation_method": "Written Down Value",
"total_number_of_depreciations": 3,
"frequency_of_depreciation": 12,
"depreciation_start_date": "2030-12-31"
})
asset.save(ignore_permissions=True)
self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
expected_schedules = [
["2030-12-31", 4000.0, 4000.0],
["2031-12-31", 2000.0, 6000.0],
["2032-12-31", 1000.0, 7000.0],
]
schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
for d in asset.get("schedules")]
self.assertEqual(schedules, expected_schedules)
def test_depreciation_entry_cancellation(self):
pr = make_purchase_receipt(item_code="Macbook Pro",
qty=1, rate=100000.0, location="Test Location")

View File

@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@ -14,11 +15,13 @@
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "finance_book",
"fieldtype": "Link",
"hidden": 0,
@ -42,14 +45,17 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "depreciation_method",
"fieldtype": "Select",
"hidden": 0,
@ -73,14 +79,17 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "total_number_of_depreciations",
"fieldtype": "Int",
"hidden": 0,
@ -103,14 +112,17 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_5",
"fieldtype": "Column Break",
"hidden": 0,
@ -133,14 +145,17 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "frequency_of_depreciation",
"fieldtype": "Int",
"hidden": 0,
@ -163,15 +178,18 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:parent.doctype == 'Asset'",
"fetch_if_empty": 0,
"fieldname": "depreciation_start_date",
"fieldtype": "Date",
"hidden": 0,
@ -194,16 +212,19 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0",
"depends_on": "eval:parent.doctype == 'Asset'",
"fetch_if_empty": 0,
"fieldname": "expected_value_after_useful_life",
"fieldtype": "Currency",
"hidden": 0,
@ -227,14 +248,17 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "value_after_depreciation",
"fieldtype": "Currency",
"hidden": 1,
@ -258,20 +282,54 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.depreciation_method == 'Written Down Value'",
"description": "In Percentage",
"fetch_if_empty": 0,
"fieldname": "rate_of_depreciation",
"fieldtype": "Percent",
"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": "Rate of Depreciation",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"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-05-12 14:56:44.800046",
"modified": "2019-04-09 19:45:14.523488",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Finance Book",
@ -280,10 +338,10 @@
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
"track_seen": 0,
"track_views": 0
}

View File

@ -36,7 +36,8 @@ frappe.ui.form.on("Purchase Order", {
},
refresh: function(frm) {
if(frm.doc.docstatus == 1 && frm.doc.status == 'To Receive and Bill') {
if(frm.doc.docstatus === 1 && frm.doc.status !== 'Closed'
&& flt(frm.doc.per_received) < 100 && flt(frm.doc.per_billed) < 100) {
frm.add_custom_button(__('Update Items'), () => {
erpnext.utils.update_child_items({
frm: frm,
@ -93,62 +94,63 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
}
}
cur_frm.set_df_property("drop_ship", "hidden", !is_drop_ship);
this.frm.set_df_property("drop_ship", "hidden", !is_drop_ship);
if(doc.docstatus == 1 && !in_list(["Closed", "Delivered"], doc.status)) {
if(doc.docstatus == 1) {
if(!in_list(["Closed", "Delivered"], doc.status)) {
if (this.frm.has_perm("submit")) {
if(flt(doc.per_billed, 2) < 100 || doc.per_received < 100) {
cur_frm.add_custom_button(__('Close'), this.close_purchase_order, __("Status"));
if (doc.status != "On Hold") {
this.frm.add_custom_button(__('Hold'), () => this.hold_purchase_order(), __("Status"));
} else{
this.frm.add_custom_button(__('Resume'), () => this.unhold_purchase_order(), __("Status"));
}
this.frm.add_custom_button(__('Close'), () => this.close_purchase_order(), __("Status"));
}
}
if(is_drop_ship && doc.status!="Delivered"){
cur_frm.add_custom_button(__('Delivered'),
if(is_drop_ship && doc.status!="Delivered") {
this.frm.add_custom_button(__('Delivered'),
this.delivered_by_supplier, __("Status"));
cur_frm.page.set_inner_btn_group_as_primary(__("Status"));
this.frm.page.set_inner_btn_group_as_primary(__("Status"));
}
} else if(doc.docstatus===0) {
cur_frm.cscript.add_from_mappers();
}
if(doc.docstatus == 1 && in_list(["Closed", "Delivered"], doc.status)) {
} else if(in_list(["Closed", "Delivered"], doc.status)) {
if (this.frm.has_perm("submit")) {
cur_frm.add_custom_button(__('Re-open'), this.unclose_purchase_order, __("Status"));
this.frm.add_custom_button(__('Re-open'), () => this.unclose_purchase_order(), __("Status"));
}
}
if(doc.docstatus == 1 && doc.status != "Closed") {
if(doc.status != "Closed") {
if (doc.status != "On Hold") {
if(flt(doc.per_received, 2) < 100 && allow_receipt) {
cur_frm.add_custom_button(__('Receipt'), this.make_purchase_receipt, __('Create'));
if(doc.is_subcontracted==="Yes") {
cur_frm.add_custom_button(__('Material to Supplier'),
function() { me.make_stock_entry(); }, __("Transfer"));
}
}
if(flt(doc.per_billed, 2) < 100)
cur_frm.add_custom_button(__('Invoice'),
this.make_purchase_invoice, __('Create'));
if(flt(doc.per_billed)==0 && doc.status != "Delivered") {
cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_payment_entry, __('Create'));
}
if(!doc.auto_repeat) {
cur_frm.add_custom_button(__('Subscription'), function() {
erpnext.utils.make_subscription(doc.doctype, doc.name)
}, __('Create'))
}
}
if(flt(doc.per_billed)==0) {
this.frm.add_custom_button(__('Payment Request'),
function() { me.make_payment_request() }, __('Create'));
}
if(flt(doc.per_billed)==0 && doc.status != "Delivered") {
cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_payment_entry, __('Create'));
}
cur_frm.page.set_inner_btn_group_as_primary(__('Create'));
}
} else if(doc.docstatus===0) {
cur_frm.cscript.add_from_mappers();
}
},
get_items_from_open_material_requests: function() {
@ -427,6 +429,43 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
}
},
unhold_purchase_order: function(){
cur_frm.cscript.update_status("Resume", "Draft")
},
hold_purchase_order: function(){
var me = this;
var d = new frappe.ui.Dialog({
title: __('Reason for Hold'),
fields: [
{
"fieldname": "reason_for_hold",
"fieldtype": "Text",
"reqd": 1,
}
],
primary_action: function() {
var data = d.get_values();
frappe.call({
method: "frappe.desk.form.utils.add_comment",
args: {
reference_doctype: me.frm.doctype,
reference_name: me.frm.docname,
content: __('Reason for hold: ')+data.reason_for_hold,
comment_email: frappe.session.user
},
callback: function(r) {
if(!r.exc) {
me.update_status('Hold', 'On Hold')
d.hide();
}
}
});
}
});
d.show();
},
unclose_purchase_order: function(){
cur_frm.cscript.update_status('Re-open', 'Submitted')
},

View File

@ -20,6 +20,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "supplier_section",
"fieldtype": "Section Break",
"hidden": 0,
@ -53,6 +54,7 @@
"collapsible": 0,
"columns": 0,
"default": "{supplier_name}",
"fetch_if_empty": 0,
"fieldname": "title",
"fieldtype": "Data",
"hidden": 1,
@ -86,6 +88,7 @@
"collapsible": 0,
"columns": 0,
"default": "",
"fetch_if_empty": 0,
"fieldname": "naming_series",
"fieldtype": "Select",
"hidden": 0,
@ -121,6 +124,7 @@
"collapsible": 0,
"columns": 0,
"description": "",
"fetch_if_empty": 0,
"fieldname": "supplier",
"fieldtype": "Link",
"hidden": 0,
@ -156,6 +160,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.supplier && doc.docstatus===0 && (!(doc.items && doc.items.length) || (doc.items.length==1 && !doc.items[0].item_code))",
"fetch_if_empty": 0,
"fieldname": "get_items_from_open_material_requests",
"fieldtype": "Button",
"hidden": 0,
@ -189,6 +194,7 @@
"collapsible": 0,
"columns": 0,
"fetch_from": "supplier.supplier_name",
"fetch_if_empty": 0,
"fieldname": "supplier_name",
"fieldtype": "Data",
"hidden": 0,
@ -222,6 +228,7 @@
"collapsible": 0,
"columns": 0,
"description": "",
"fetch_if_empty": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
@ -256,6 +263,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break1",
"fieldtype": "Column Break",
"hidden": 0,
@ -3310,7 +3318,7 @@
"no_copy": 1,
"oldfieldname": "status",
"oldfieldtype": "Select",
"options": "\nDraft\nTo Receive and Bill\nTo Bill\nTo Receive\nCompleted\nCancelled\nClosed\nDelivered",
"options": "\nDraft\nOn Hold\nTo Receive and Bill\nTo Bill\nTo Receive\nCompleted\nCancelled\nClosed\nDelivered",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
@ -3531,6 +3539,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "letter_head",
"fieldtype": "Link",
"hidden": 0,
@ -3565,6 +3574,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "select_print_heading",
"fieldtype": "Link",
"hidden": 0,
@ -3599,6 +3609,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_86",
"fieldtype": "Column Break",
"hidden": 0,
@ -3631,6 +3642,7 @@
"collapsible": 0,
"columns": 0,
"description": "",
"fetch_if_empty": 0,
"fieldname": "group_same_items",
"fieldtype": "Check",
"hidden": 0,
@ -3664,6 +3676,7 @@
"collapsible": 0,
"columns": 0,
"default": "",
"fetch_if_empty": 0,
"fieldname": "language",
"fieldtype": "Data",
"hidden": 0,
@ -3697,6 +3710,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "subscription_section",
"fieldtype": "Section Break",
"hidden": 0,
@ -3731,6 +3745,7 @@
"columns": 0,
"depends_on": "",
"description": "",
"fetch_if_empty": 0,
"fieldname": "from_date",
"fieldtype": "Date",
"hidden": 0,
@ -3764,6 +3779,7 @@
"columns": 0,
"depends_on": "",
"description": "",
"fetch_if_empty": 0,
"fieldname": "to_date",
"fieldtype": "Date",
"hidden": 0,
@ -3795,6 +3811,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_97",
"fieldtype": "Column Break",
"hidden": 0,
@ -3826,6 +3843,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "auto_repeat",
"fieldtype": "Link",
"hidden": 0,
@ -3860,6 +3878,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval: doc.auto_repeat",
"fetch_if_empty": 0,
"fieldname": "update_auto_repeat_reference",
"fieldtype": "Button",
"hidden": 0,

View File

@ -11,7 +11,7 @@ from erpnext.controllers.buying_controller import BuyingController
from erpnext.stock.doctype.item.item import get_last_purchase_details
from erpnext.stock.stock_balance import update_bin_qty, get_ordered_qty
from frappe.desk.notifications import clear_doctype_notifications
from erpnext.buying.utils import validate_for_items, check_for_closed_status
from erpnext.buying.utils import validate_for_items, check_on_hold_or_closed_status
from erpnext.stock.utils import get_bin
from erpnext.accounts.party import get_party_account_currency
from six import string_types
@ -45,7 +45,7 @@ class PurchaseOrder(BuyingController):
self.validate_supplier()
self.validate_schedule_date()
validate_for_items(self)
self.check_for_closed_status()
self.check_on_hold_or_closed_status()
self.validate_uom_is_integer("uom", "qty")
self.validate_uom_is_integer("stock_uom", "stock_qty")
@ -144,12 +144,12 @@ class PurchaseOrder(BuyingController):
= d.rate = d.last_purchase_rate = item_last_purchase_rate
# Check for Closed status
def check_for_closed_status(self):
def check_on_hold_or_closed_status(self):
check_list =[]
for d in self.get('items'):
if d.meta.get_field('material_request') and d.material_request and d.material_request not in check_list:
check_list.append(d.material_request)
check_for_closed_status('Material Request', d.material_request)
check_on_hold_or_closed_status('Material Request', d.material_request)
def update_requested_qty(self):
material_request_map = {}
@ -232,7 +232,7 @@ class PurchaseOrder(BuyingController):
if self.is_subcontracted == "Yes":
self.update_reserved_qty_for_subcontract()
self.check_for_closed_status()
self.check_on_hold_or_closed_status()
frappe.db.set(self,'status','Cancelled')
@ -382,6 +382,8 @@ def make_purchase_invoice(source_name, target_doc=None):
def postprocess(source, target):
set_missing_values(source, target)
#Get the advance paid Journal Entries in Purchase Invoice Advance
if target.get("allocate_advances_automatically"):
target.set_advances()
def update_item(obj, target, source_parent):

View File

@ -4,6 +4,8 @@ frappe.listview_settings['Purchase Order'] = {
get_indicator: function (doc) {
if (doc.status === "Closed") {
return [__("Closed"), "green", "status,=,Closed"];
} else if (doc.status === "On Hold") {
return [__("On Hold"), "orange", "status,=,On Hold"];
} else if (doc.status === "Delivered") {
return [__("Delivered"), "green", "status,=,Closed"];
} else if (flt(doc.per_received, 2) < 100 && doc.status !== "Closed") {

View File

@ -120,6 +120,15 @@ class TestPurchaseOrder(unittest.TestCase):
self.assertEqual(pi.doctype, "Purchase Invoice")
self.assertEqual(len(pi.get("items", [])), 1)
def test_purchase_order_on_hold(self):
po = create_purchase_order(item_code="_Test Product Bundle Item")
po.db_set('Status', "On Hold")
pi = make_purchase_invoice(po.name)
pr = make_purchase_receipt(po.name)
self.assertRaises(frappe.ValidationError, pr.submit)
self.assertRaises(frappe.ValidationError, pi.submit)
def test_make_purchase_invoice_with_terms(self):
po = create_purchase_order(do_not_save=True)

View File

@ -1,18 +1,19 @@
{
"add_total_row": 1,
"apply_user_permissions": 1,
"creation": "2013-05-13 16:10:02",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 3,
"is_standard": "Yes",
"modified": "2017-02-24 20:10:53.005589",
"modified": "2019-04-18 19:02:03.099422",
"modified_by": "Administrator",
"module": "Buying",
"name": "Requested Items To Be Ordered",
"owner": "Administrator",
"query": "select \n mr.name as \"Material Request:Link/Material Request:120\",\n\tmr.transaction_date as \"Date:Date:100\",\n\tmr_item.item_code as \"Item Code:Link/Item:120\",\n\tsum(ifnull(mr_item.qty, 0)) as \"Qty:Float:100\",\n\tsum(ifnull(mr_item.ordered_qty, 0)) as \"Ordered Qty:Float:100\", \n\t(sum(mr_item.qty) - sum(ifnull(mr_item.ordered_qty, 0))) as \"Qty to Order:Float:100\",\n\tmr_item.item_name as \"Item Name::150\",\n\tmr_item.description as \"Description::200\",\n\tmr.company as \"Company:Link/Company:\"\nfrom\n\t`tabMaterial Request` mr, `tabMaterial Request Item` mr_item\nwhere\n\tmr_item.parent = mr.name\n\tand mr.material_request_type = \"Purchase\"\n\tand mr.docstatus = 1\n\tand mr.status != \"Stopped\"\ngroup by mr.name, mr_item.item_code\nhaving\n\tsum(ifnull(mr_item.ordered_qty, 0)) < sum(ifnull(mr_item.qty, 0))\norder by mr.transaction_date asc",
"prepared_report": 0,
"query": "select \n mr.name as \"Material Request:Link/Material Request:120\",\n\tmr.transaction_date as \"Date:Date:100\",\n\tmr_item.item_code as \"Item Code:Link/Item:120\",\n\tsum(ifnull(mr_item.stock_qty, 0)) as \"Qty:Float:100\",\n\tifnull(mr_item.stock_uom, '') as \"UOM:Link/UOM:100\",\n\tsum(ifnull(mr_item.ordered_qty, 0)) as \"Ordered Qty:Float:100\", \n\t(sum(mr_item.stock_qty) - sum(ifnull(mr_item.ordered_qty, 0))) as \"Qty to Order:Float:100\",\n\tmr_item.item_name as \"Item Name::150\",\n\tmr_item.description as \"Description::200\",\n\tmr.company as \"Company:Link/Company:\"\nfrom\n\t`tabMaterial Request` mr, `tabMaterial Request Item` mr_item\nwhere\n\tmr_item.parent = mr.name\n\tand mr.material_request_type = \"Purchase\"\n\tand mr.docstatus = 1\n\tand mr.status != \"Stopped\"\ngroup by mr.name, mr_item.item_code\nhaving\n\tsum(ifnull(mr_item.ordered_qty, 0)) < sum(ifnull(mr_item.stock_qty, 0))\norder by mr.transaction_date asc",
"ref_doctype": "Purchase Order",
"report_name": "Requested Items To Be Ordered",
"report_type": "Query Report",

View File

@ -73,10 +73,10 @@ def validate_for_items(doc):
not cint(frappe.db.get_single_value("Buying Settings", "allow_multiple_items") or 0):
frappe.throw(_("Same item cannot be entered multiple times."))
def check_for_closed_status(doctype, docname):
def check_on_hold_or_closed_status(doctype, docname):
status = frappe.db.get_value(doctype, docname, "status")
if status == "Closed":
if status in ("Closed", "On Hold"):
frappe.throw(_("{0} {1} status is {2}").format(doctype, docname, status), frappe.InvalidStatusError)
@frappe.whitelist()

View File

@ -78,6 +78,11 @@ def get_data():
"name": "Payment Entry",
"description": _("Bank/Cash transactions against party or for internal transfer")
},
{
"type": "doctype",
"name": "Payment Term",
"description": _("Payment Terms based on conditions")
},
# Reports
{
@ -131,6 +136,11 @@ def get_data():
"name": "Currency Exchange",
"description": _("Currency exchange rate master.")
},
{
"type": "doctype",
"name": "Exchange Rate Revaluation",
"description": _("Exchange Rate Revaluation master.")
},
{
"type": "doctype",
"name": "Payment Gateway Account",
@ -232,6 +242,11 @@ def get_data():
"label": _("Bank Account"),
"name": "Bank Account",
},
{
"type": "doctype",
"label": _("Invoice Discounting"),
"name": "Invoice Discounting",
},
{
"type": "doctype",
"label": _("Bank Statement Transaction Entry List"),

View File

@ -442,6 +442,13 @@ class BuyingController(StockController):
frappe.throw(_("Row #{0}: {1} can not be negative for item {2}".format(item_row['idx'],
frappe.get_meta(item_row.doctype).get_label(fieldname), item_row['item_code'])))
def check_for_on_hold_or_closed_status(self, ref_doctype, ref_fieldname):
for d in self.get("items"):
if d.get(ref_fieldname):
status = frappe.db.get_value(ref_doctype, d.get(ref_fieldname), "status")
if status in ("Closed", "On Hold"):
frappe.throw(_("{0} {1} is {2}").format(ref_doctype,d.get(ref_fieldname), status))
def update_stock_ledger(self, allow_negative_stock=False, via_landed_cost_voucher=False):
self.update_ordered_and_reserved_qty()

View File

@ -2,8 +2,9 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
import frappe, erpnext
from frappe import _
from frappe.model.meta import get_field_precision
from frappe.utils import flt, get_datetime, format_datetime
class StockOverReturnError(frappe.ValidationError): pass
@ -116,6 +117,10 @@ def validate_quantity(doc, args, ref, valid_items, already_returned_items):
already_returned_data = already_returned_items.get(args.item_code) or {}
company_currency = erpnext.get_company_currency(doc.company)
stock_qty_precision = get_field_precision(frappe.get_meta(doc.doctype + " Item")
.get_field("stock_qty"), company_currency)
for column in fields:
returned_qty = flt(already_returned_data.get(column, 0)) if len(already_returned_data) > 0 else 0
@ -126,7 +131,7 @@ def validate_quantity(doc, args, ref, valid_items, already_returned_items):
reference_qty = ref.get(column) * ref.get("conversion_factor", 1.0)
current_stock_qty = args.get(column) * args.get("conversion_factor", 1.0)
max_returnable_qty = flt(reference_qty) - returned_qty
max_returnable_qty = flt(reference_qty, stock_qty_precision) - returned_qty
label = column.replace('_', ' ').title()
if reference_qty:
@ -135,7 +140,7 @@ def validate_quantity(doc, args, ref, valid_items, already_returned_items):
elif returned_qty >= reference_qty and args.get(column):
frappe.throw(_("Item {0} has already been returned")
.format(args.item_code), StockOverReturnError)
elif abs(current_stock_qty) > max_returnable_qty:
elif abs(flt(current_stock_qty, stock_qty_precision)) > max_returnable_qty:
frappe.throw(_("Row # {0}: Cannot return more than {1} for Item {2}")
.format(args.idx, max_returnable_qty, args.item_code), StockOverReturnError)
@ -239,6 +244,10 @@ def make_return_doc(doctype, source_name, target_doc=None):
doc.paid_amount = -1 * source.paid_amount
doc.base_paid_amount = -1 * source.base_paid_amount
if doc.get("is_return") and hasattr(doc, "packed_items"):
for d in doc.get("packed_items"):
d.qty = d.qty * -1
doc.discount_amount = -1 * source.discount_amount
doc.run_method("calculate_taxes_and_totals")

View File

@ -257,11 +257,11 @@ class SellingController(StockController):
so_warehouse = so_item and so_item[0]["warehouse"] or ""
return so_qty, so_warehouse
def check_close_sales_order(self, ref_fieldname):
def check_sales_order_on_hold_or_close(self, ref_fieldname):
for d in self.get("items"):
if d.get(ref_fieldname):
status = frappe.db.get_value("Sales Order", d.get(ref_fieldname), "status")
if status == "Closed":
if status in ("Closed", "On Hold"):
frappe.throw(_("Sales Order {0} is {1}").format(d.get(ref_fieldname), status))
def update_reserved_qty(self):

View File

@ -41,6 +41,7 @@ status_map = {
["Completed", "eval:self.order_type == 'Maintenance' and self.per_billed == 100 and self.docstatus == 1"],
["Cancelled", "eval:self.docstatus==2"],
["Closed", "eval:self.status=='Closed'"],
["On Hold", "eval:self.status=='On Hold'"],
],
"Sales Invoice": [
["Draft", None],
@ -70,6 +71,7 @@ status_map = {
["Completed", "eval:self.per_received == 100 and self.per_billed == 100 and self.docstatus == 1"],
["Delivered", "eval:self.status=='Delivered'"],
["Cancelled", "eval:self.docstatus==2"],
["On Hold", "eval:self.status=='On Hold'"],
["Closed", "eval:self.status=='Closed'"],
],
"Delivery Note": [

View File

@ -42,10 +42,10 @@ def create_variant_with_tables(item, args):
return variant
def make_item_variant():
frappe.delete_doc_if_exists("Item", "_Test Variant Item-S", force=1)
variant = create_variant_with_tables("_Test Variant Item", '{"Test Size": "Small"}')
variant.item_code = "_Test Variant Item-S"
variant.item_name = "_Test Variant Item-S"
frappe.delete_doc_if_exists("Item", "_Test Variant Item-XSL", force=1)
variant = create_variant_with_tables("_Test Variant Item", '{"Test Size": "Extra Small"}')
variant.item_code = "_Test Variant Item-XSL"
variant.item_name = "_Test Variant Item-XSL"
variant.save()
return variant

View File

@ -21,6 +21,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "organization_lead",
"fieldtype": "Check",
"hidden": 0,
@ -53,6 +54,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "lead_details",
"fieldtype": "Section Break",
"hidden": 0,
@ -86,6 +88,7 @@
"collapsible": 0,
"columns": 0,
"default": "",
"fetch_if_empty": 0,
"fieldname": "naming_series",
"fieldtype": "Select",
"hidden": 0,
@ -121,6 +124,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:!doc.organization_lead",
"fetch_if_empty": 0,
"fieldname": "lead_name",
"fieldtype": "Data",
"hidden": 0,
@ -154,6 +158,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "company_name",
"fieldtype": "Data",
"hidden": 0,
@ -187,6 +192,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "email_id",
"fieldtype": "Data",
"hidden": 0,
@ -221,6 +227,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "col_break123",
"fieldtype": "Column Break",
"hidden": 0,
@ -245,6 +252,42 @@
"unique": 0,
"width": "50%"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "__user",
"fetch_if_empty": 0,
"fieldname": "lead_owner",
"fieldtype": "Link",
"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": "Lead Owner",
"length": 0,
"no_copy": 0,
"oldfieldname": "lead_owner",
"oldfieldtype": "Link",
"options": "User",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@ -253,6 +296,7 @@
"collapsible": 0,
"columns": 0,
"default": "Lead",
"fetch_if_empty": 0,
"fieldname": "status",
"fieldtype": "Select",
"hidden": 0,
@ -288,6 +332,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:!doc.organization_lead",
"fetch_if_empty": 0,
"fieldname": "gender",
"fieldtype": "Link",
"hidden": 0,
@ -322,6 +367,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "source",
"fieldtype": "Link",
"hidden": 0,
@ -357,6 +403,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.source == 'Existing Customer'",
"fetch_if_empty": 0,
"fieldname": "customer",
"fieldtype": "Link",
"hidden": 0,
@ -393,6 +440,7 @@
"columns": 0,
"depends_on": "eval: doc.source==\"Campaign\"",
"description": "",
"fetch_if_empty": 0,
"fieldname": "campaign_name",
"fieldtype": "Link",
"hidden": 0,
@ -427,6 +475,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "image",
"fieldtype": "Attach Image",
"hidden": 1,
@ -459,6 +508,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "section_break_12",
"fieldtype": "Section Break",
"hidden": 0,
@ -491,41 +541,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "__user",
"fieldname": "lead_owner",
"fieldtype": "Link",
"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": "Lead Owner",
"length": 0,
"no_copy": 0,
"oldfieldname": "lead_owner",
"oldfieldtype": "Link",
"options": "User",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "contact_by",
"fieldtype": "Link",
"hidden": 0,
@ -561,6 +577,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_14",
"fieldtype": "Column Break",
"hidden": 0,
@ -593,6 +610,7 @@
"collapsible": 0,
"columns": 0,
"description": "",
"fetch_if_empty": 0,
"fieldname": "contact_date",
"fieldtype": "Datetime",
"hidden": 0,
@ -627,6 +645,7 @@
"bold": 1,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "ends_on",
"fieldtype": "Datetime",
"hidden": 0,
@ -659,6 +678,7 @@
"bold": 0,
"collapsible": 1,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "notes_section",
"fieldtype": "Section Break",
"hidden": 0,
@ -691,6 +711,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "notes",
"fieldtype": "Text Editor",
"hidden": 0,
@ -723,6 +744,7 @@
"bold": 0,
"collapsible": 1,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "contact_info",
"fieldtype": "Section Break",
"hidden": 0,
@ -757,6 +779,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.__islocal",
"fetch_if_empty": 0,
"fieldname": "address_desc",
"fieldtype": "HTML",
"hidden": 0,
@ -788,6 +811,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "address_html",
"fieldtype": "HTML",
"hidden": 0,
@ -819,6 +843,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break2",
"fieldtype": "Column Break",
"hidden": 0,
@ -850,6 +875,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.organization_lead",
"fetch_if_empty": 0,
"fieldname": "contact_html",
"fieldtype": "HTML",
"hidden": 0,
@ -883,6 +909,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:!doc.organization_lead",
"fetch_if_empty": 0,
"fieldname": "phone",
"fieldtype": "Data",
"hidden": 0,
@ -917,6 +944,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:!doc.organization_lead",
"fetch_if_empty": 0,
"fieldname": "salutation",
"fieldtype": "Link",
"hidden": 0,
@ -951,6 +979,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:!doc.organization_lead",
"fetch_if_empty": 0,
"fieldname": "mobile_no",
"fieldtype": "Data",
"hidden": 0,
@ -985,6 +1014,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:!doc.organization_lead",
"fetch_if_empty": 0,
"fieldname": "fax",
"fieldtype": "Data",
"hidden": 0,
@ -1018,6 +1048,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "website",
"fieldtype": "Data",
"hidden": 0,
@ -1052,6 +1083,7 @@
"collapsible": 0,
"columns": 0,
"description": "",
"fetch_if_empty": 0,
"fieldname": "territory",
"fieldtype": "Link",
"hidden": 0,
@ -1086,6 +1118,7 @@
"bold": 0,
"collapsible": 1,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "more_info",
"fieldtype": "Section Break",
"hidden": 0,
@ -1119,6 +1152,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "type",
"fieldtype": "Select",
"hidden": 0,
@ -1153,6 +1187,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "market_segment",
"fieldtype": "Link",
"hidden": 0,
@ -1187,6 +1222,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "industry",
"fieldtype": "Link",
"hidden": 0,
@ -1221,6 +1257,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "request_type",
"fieldtype": "Select",
"hidden": 0,
@ -1255,6 +1292,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break3",
"fieldtype": "Column Break",
"hidden": 0,
@ -1287,6 +1325,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
@ -1321,6 +1360,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "unsubscribed",
"fieldtype": "Check",
"hidden": 0,
@ -1352,6 +1392,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "blog_subscriber",
"fieldtype": "Check",
"hidden": 0,
@ -1378,18 +1419,17 @@
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-user",
"idx": 5,
"image_field": "image",
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-10-02 09:12:23.415379",
"menu_index": 0,
"modified": "2019-04-11 22:12:50.029368",
"modified_by": "Administrator",
"module": "CRM",
"name": "Lead",
@ -1513,7 +1553,6 @@
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"search_fields": "lead_name,lead_owner,status",
"show_name_in_global_search": 1,
"sort_field": "modified",

View File

@ -10,6 +10,7 @@ from frappe.model.mapper import get_mapped_doc
from erpnext.controllers.selling_controller import SellingController
from frappe.contacts.address_and_contact import load_address_and_contact
from erpnext.accounts.party import set_taxes
from frappe.email.inbox import link_communication_to_document
sender_field = "email_id"
@ -199,3 +200,29 @@ def get_lead_details(lead, posting_date=None, company=None):
out['taxes_and_charges'] = taxes_and_charges
return out
@frappe.whitelist()
def make_lead_from_communication(communication, ignore_communication_links=False):
""" raise a issue from email """
doc = frappe.get_doc("Communication", communication)
lead_name = None
if doc.sender:
lead_name = frappe.db.get_value("Lead", {"email_id": doc.sender})
if not lead_name and doc.phone_no:
lead_name = frappe.db.get_value("Lead", {"mobile_no": doc.phone_no})
if not lead_name:
lead = frappe.get_doc({
"doctype": "Lead",
"lead_name": doc.sender_full_name,
"email_id": doc.sender,
"mobile_no": doc.phone_no
})
lead.flags.ignore_mandatory = True
lead.flags.ignore_permissions = True
lead.insert()
lead_name = lead.name
link_communication_to_document(doc, "Lead", lead_name, ignore_communication_links)
return lead_name

View File

@ -9,6 +9,8 @@ from frappe.model.mapper import get_mapped_doc
from erpnext.setup.utils import get_exchange_rate
from erpnext.utilities.transaction_base import TransactionBase
from erpnext.accounts.party import get_party_account_currency
from frappe.desk.form import assign_to
from frappe.email.inbox import link_communication_to_document
subject_field = "title"
sender_field = "contact_email"
@ -153,6 +155,9 @@ class Opportunity(TransactionBase):
def on_update(self):
self.add_calendar_event()
# assign to customer account manager or lead owner
assign_to_user(self, subject_field)
def add_calendar_event(self, opts=None, force=False):
if not opts:
opts = frappe._dict()
@ -329,3 +334,39 @@ def auto_close_opportunity():
doc.flags.ignore_permissions = True
doc.flags.ignore_mandatory = True
doc.save()
def assign_to_user(doc, subject_field):
assign_user = None
if doc.customer:
assign_user = frappe.db.get_value('Customer', doc.customer, 'account_manager')
elif doc.lead:
assign_user = frappe.db.get_value('Lead', doc.lead, 'lead_owner')
if assign_user and assign_user != 'Administrator':
if not assign_to.get(dict(doctype = doc.doctype, name = doc.name)):
assign_to.add({
"assign_to": assign_user,
"doctype": doc.doctype,
"name": doc.name,
"description": doc.get(subject_field)
})
@frappe.whitelist()
def make_opportunity_from_communication(communication, ignore_communication_links=False):
from erpnext.crm.doctype.lead.lead import make_lead_from_communication
doc = frappe.get_doc("Communication", communication)
lead = doc.reference_name if doc.reference_doctype == "Lead" else None
if not lead:
lead = make_lead_from_communication(communication, ignore_communication_links=True)
enquiry_from = "Lead"
opportunity = frappe.get_doc({
"doctype": "Opportunity",
"enquiry_from": enquiry_from,
"lead": lead
}).insert(ignore_permissions=True)
link_communication_to_document(doc, "Opportunity", opportunity.name, ignore_communication_links)
return opportunity.name

View File

@ -3,10 +3,11 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import today
from frappe.utils import today, random_string
from erpnext.crm.doctype.lead.lead import make_customer
from erpnext.crm.doctype.opportunity.opportunity import make_quotation
import unittest
from frappe.desk.form import assign_to
test_records = frappe.get_test_records('Opportunity')
@ -27,9 +28,10 @@ class TestOpportunity(unittest.TestCase):
self.assertEqual(doc.status, "Quotation")
def test_make_new_lead_if_required(self):
new_lead_email_id = "new{}@example.com".format(random_string(5))
args = {
"doctype": "Opportunity",
"contact_email":"new.opportunity@example.com",
"contact_email": new_lead_email_id,
"opportunity_type": "Sales",
"with_items": 0,
"transaction_date": today()
@ -40,13 +42,13 @@ class TestOpportunity(unittest.TestCase):
self.assertTrue(opp_doc.lead)
self.assertEqual(opp_doc.enquiry_from, "Lead")
self.assertEqual(frappe.db.get_value("Lead", opp_doc.lead, "email_id"),
'new.opportunity@example.com')
new_lead_email_id)
# create new customer and create new contact against 'new.opportunity@example.com'
customer = make_customer(opp_doc.lead).insert(ignore_permissions=True)
frappe.get_doc({
"doctype": "Contact",
"email_id": "new.opportunity@example.com",
"email_id": new_lead_email_id,
"first_name": "_Test Opportunity Customer",
"links": [{
"link_doctype": "Customer",
@ -59,6 +61,21 @@ class TestOpportunity(unittest.TestCase):
self.assertEqual(opp_doc.enquiry_from, "Customer")
self.assertEqual(opp_doc.customer, customer.name)
def test_assignment(self):
# assign cutomer account manager
frappe.db.set_value('Customer', '_Test Customer', 'account_manager', 'test1@example.com')
doc = make_opportunity(with_items=0)
self.assertEqual(assign_to.get(dict(doctype = doc.doctype, name = doc.name))[0].get('owner'), 'test1@example.com')
# assign lead owner
frappe.db.set_value('Customer', '_Test Customer', 'account_manager', '')
frappe.db.set_value('Lead', '_T-Lead-00001', 'lead_owner', 'test2@example.com')
doc = make_opportunity(with_items=0, enquiry_from='Lead')
self.assertEqual(assign_to.get(dict(doctype = doc.doctype, name = doc.name))[0].get('owner'), 'test2@example.com')
def make_opportunity(**args):
args = frappe._dict(args)
@ -75,7 +92,7 @@ def make_opportunity(**args):
opp_doc.customer = args.customer or "_Test Customer"
if opp_doc.enquiry_from == 'Lead':
opp_doc.customer = args.lead or "_T-Lead-00001"
opp_doc.lead = args.lead or "_T-Lead-00001"
if args.with_items:
opp_doc.append('items', {

View File

@ -19,27 +19,24 @@ def verify_request():
frappe.get_request_header("X-Wc-Webhook-Signature") and \
not sig == bytes(frappe.get_request_header("X-Wc-Webhook-Signature").encode()):
frappe.throw(_("Unverified Webhook Data"))
frappe.set_user(woocommerce_settings.modified_by)
frappe.set_user(woocommerce_settings.creation_user)
@frappe.whitelist(allow_guest=True)
def order(data=None):
if not data:
verify_request()
def order():
woocommerce_settings = frappe.get_doc("Woocommerce Settings")
if frappe.flags.woocomm_test_order_data:
fd = frappe.flags.woocomm_test_order_data
event = "created"
if frappe.request and frappe.request.data:
elif frappe.request and frappe.request.data:
verify_request()
fd = json.loads(frappe.request.data)
elif data:
fd = data
event = frappe.get_request_header("X-Wc-Webhook-Event")
else:
return "success"
if not data:
event = frappe.get_request_header("X-Wc-Webhook-Event")
else:
event = "created"
if event == "created":
raw_billing_data = fd.get("billing")
customer_woo_com_email = raw_billing_data.get("email")
@ -73,7 +70,7 @@ def order(data=None):
new_sales_order.po_no = fd.get("id")
new_sales_order.woocommerce_id = fd.get("id")
new_sales_order.naming_series = "SO-"
new_sales_order.naming_series = woocommerce_settings.sales_order_series or "SO-WOO-"
placed_order_date = created_date[0]
raw_date = datetime.datetime.strptime(placed_order_date, "%Y-%m-%d")
@ -100,10 +97,10 @@ def order(data=None):
"item_name": found_item.item_name,
"description": found_item.item_name,
"delivery_date":order_delivery_date,
"uom": "Nos",
"uom": woocommerce_settings.uom or _("Nos"),
"qty": item.get("quantity"),
"rate": item.get("price"),
"warehouse": "Stores" + " - " + company_abbr
"warehouse": woocommerce_settings.warehouse or "Stores" + " - " + company_abbr
})
add_tax_details(new_sales_order,ordered_items_tax,"Ordered Item tax",0)
@ -175,6 +172,7 @@ def link_customer_and_address(raw_billing_data,customer_status):
frappe.db.commit()
def link_item(item_data,item_status):
woocommerce_settings = frappe.get_doc("Woocommerce Settings")
if item_status == 0:
#Create Item
@ -189,6 +187,7 @@ def link_item(item_data,item_status):
item.item_code = "woocommerce - " + str(item_data.get("product_id"))
item.woocommerce_id = str(item_data.get("product_id"))
item.item_group = "WooCommerce Products"
item.stock_uom = woocommerce_settings.uom or _("Nos")
item.save()
frappe.db.commit()

View File

@ -43,3 +43,14 @@ frappe.ui.form.on('Woocommerce Settings', {
frm.set_df_property("api_consumer_secret", "reqd", frm.doc.enable_sync);
}
});
frappe.ui.form.on("Woocommerce Settings", "onload", function () {
frappe.call({
method: "erpnext.erpnext_integrations.doctype.woocommerce_settings.woocommerce_settings.get_series",
callback: function (r) {
$.each(r.message, function (key, value) {
set_field_options(key, value);
});
}
});
});

View File

@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@ -14,10 +15,12 @@
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "enable_sync",
"fieldtype": "Check",
"hidden": 0,
@ -45,10 +48,12 @@
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "sb_00",
"fieldtype": "Section Break",
"hidden": 0,
@ -76,10 +81,12 @@
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "woocommerce_server_url",
"fieldtype": "Data",
"hidden": 0,
@ -107,10 +114,12 @@
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "secret",
"fieldtype": "Code",
"hidden": 0,
@ -138,10 +147,12 @@
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "cb_00",
"fieldtype": "Column Break",
"hidden": 0,
@ -168,10 +179,12 @@
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "api_consumer_key",
"fieldtype": "Data",
"hidden": 0,
@ -199,10 +212,12 @@
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "api_consumer_secret",
"fieldtype": "Data",
"hidden": 0,
@ -230,10 +245,12 @@
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "sb_accounting_details",
"fieldtype": "Section Break",
"hidden": 0,
@ -261,10 +278,12 @@
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "tax_account",
"fieldtype": "Link",
"hidden": 0,
@ -293,10 +312,12 @@
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_10",
"fieldtype": "Column Break",
"hidden": 0,
@ -323,10 +344,12 @@
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "f_n_f_account",
"fieldtype": "Link",
"hidden": 0,
@ -355,10 +378,216 @@
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "defaults_section",
"fieldtype": "Section Break",
"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": "Defaults",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "The user that will be used to create Customers, Items and Sales Orders. This user should have the relevant permissions.",
"fetch_if_empty": 0,
"fieldname": "creation_user",
"fieldtype": "Link",
"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": "Creation User",
"length": 0,
"no_copy": 0,
"options": "User",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "This warehouse will be used to create Sale Orders. The fallback warehouse is \"Stores\".",
"fetch_if_empty": 0,
"fieldname": "warehouse",
"fieldtype": "Link",
"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": "Warehouse",
"length": 0,
"no_copy": 0,
"options": "Warehouse",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_14",
"fieldtype": "Column Break",
"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,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "The fallback series is \"SO-WOO-\".",
"fetch_if_empty": 0,
"fieldname": "sales_order_series",
"fieldtype": "Select",
"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": "Sales Order Series",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "This is the default UOM used for items and Sales orders. The fallback UOM is \"Nos\".",
"fetch_if_empty": 0,
"fieldname": "uom",
"fieldtype": "Link",
"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": "UOM",
"length": 0,
"no_copy": 0,
"options": "UOM",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "endpoints",
"fieldtype": "Section Break",
"hidden": 0,
@ -386,10 +615,12 @@
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "endpoint",
"fieldtype": "Code",
"hidden": 0,
@ -417,16 +648,15 @@
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2018-03-23 16:57:20.880513",
"menu_index": 0,
"modified": "2019-04-08 17:04:16.720696",
"modified_by": "Administrator",
"module": "ERPNext Integrations",
"name": "Woocommerce Settings",
@ -435,7 +665,6 @@
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 0,
@ -456,10 +685,10 @@
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
"track_seen": 0,
"track_views": 0
}

View File

@ -65,7 +65,7 @@ class WoocommerceSettings(Document):
if not frappe.get_value("Item Group",{"name": "WooCommerce Products"}):
item_group = frappe.new_doc("Item Group")
item_group.item_group_name = "WooCommerce Products"
item_group.parent_item_group = "All Item Groups"
item_group.parent_item_group = _("All Item Groups")
item_group.save()
@ -122,3 +122,9 @@ def generate_secret():
woocommerce_settings = frappe.get_doc("Woocommerce Settings")
woocommerce_settings.secret = frappe.generate_hash()
woocommerce_settings.save()
@frappe.whitelist()
def get_series():
return {
"sales_order_series" : frappe.get_meta("Sales Order").get_options("naming_series") or "SO-WOO-",
}

View File

@ -111,7 +111,7 @@ def change_item_code(item, item_code, doc_name):
frappe.db.set_value("Healthcare Service Unit Type", doc_name, "item_code", item_code)
@frappe.whitelist()
def disable_enable(status, doc_name, item, is_billable):
def disable_enable(status, doc_name, item=None, is_billable=None):
frappe.db.set_value("Healthcare Service Unit Type", doc_name, "disabled", status)
if(is_billable == 1):
frappe.db.set_value("Item", item, "disabled", status)

View File

@ -51,7 +51,7 @@ def get_additional_salary_component(employee, start_date, end_date):
for d in additional_components:
component = frappe.get_doc("Salary Component", d.salary_component)
struct_row = {'salary_component': d.salary_component}
for field in ["depends_on_lwp", "abbr", "is_tax_applicable", "variable_based_on_taxable_salary", "is_additional_component"]:
for field in ["depends_on_payment_days", "abbr", "is_tax_applicable", "variable_based_on_taxable_salary", "is_additional_component"]:
struct_row[field] = component.get(field)
additional_components_list.append({

View File

@ -80,6 +80,14 @@ class Employee(NestedSet):
if not self.create_user_permission: return
if not has_permission('User Permission', ptype='write'): return
employee_user_permission_exists = frappe.db.exists('User Permission', {
'allow': 'Employee',
'for_value': self.name,
'user': self.user_id
})
if employee_user_permission_exists: return
add_user_permission("Employee", self.name, self.user_id)
set_user_permission_if_allowed("Company", self.company, self.user_id)

View File

@ -113,7 +113,7 @@ def get_max_benefits(employee, on_date):
def get_max_benefits_remaining(employee, on_date, payroll_period):
max_benefits = get_max_benefits(employee, on_date)
if max_benefits and max_benefits > 0:
have_depends_on_lwp = False
have_depends_on_payment_days = False
per_day_amount_total = 0
payroll_period_days = get_payroll_period_days(on_date, on_date, employee)[0]
payroll_period_obj = frappe.get_doc("Payroll Period", payroll_period)
@ -122,22 +122,22 @@ def get_max_benefits_remaining(employee, on_date, payroll_period):
prev_sal_slip_flexi_total = get_sal_slip_total_benefit_given(employee, payroll_period_obj)
if prev_sal_slip_flexi_total > 0:
# Check salary structure hold depends_on_lwp component
# Check salary structure hold depends_on_payment_days component
# If yes then find the amount per day of each component and find the sum
sal_struct_name = get_assigned_salary_structure(employee, on_date)
if sal_struct_name:
sal_struct = frappe.get_doc("Salary Structure", sal_struct_name)
for sal_struct_row in sal_struct.get("earnings"):
salary_component = frappe.get_doc("Salary Component", sal_struct_row.salary_component)
if salary_component.depends_on_lwp == 1 and salary_component.pay_against_benefit_claim != 1:
have_depends_on_lwp = True
if salary_component.depends_on_payment_days == 1 and salary_component.pay_against_benefit_claim != 1:
have_depends_on_payment_days = True
benefit_amount = get_benefit_pro_rata_ratio_amount(sal_struct, salary_component.max_benefit_amount)
amount_per_day = benefit_amount / payroll_period_days
per_day_amount_total += amount_per_day
# Then the sum multiply with the no of lwp in that period
# Include that amount to the prev_sal_slip_flexi_total to get the actual
if have_depends_on_lwp and per_day_amount_total > 0:
if have_depends_on_payment_days and per_day_amount_total > 0:
holidays = get_holidays_for_employee(employee, payroll_period_obj.start_date, on_date)
working_days = date_diff(on_date, payroll_period_obj.start_date) + 1
leave_days = calculate_lwp(employee, payroll_period_obj.start_date, holidays, working_days)
@ -185,7 +185,7 @@ def get_benefit_component_amount(employee, start_date, end_date, struct_row, sal
'payroll_period': payroll_period
})
if frappe.db.get_value("Salary Component", struct_row.salary_component, "depends_on_lwp") != 1:
if frappe.db.get_value("Salary Component", struct_row.salary_component, "depends_on_payment_days") != 1:
if frequency == "Monthly" and actual_payroll_days in range(360, 370):
period_length = 1
period_factor = 12

View File

@ -170,7 +170,7 @@ def get_last_payroll_period_benefits(employee, sal_slip_start_date, sal_slip_end
amount += current_claimed_amount
struct_row = {}
salary_components_dict = {}
struct_row['depends_on_lwp'] = salary_component.depends_on_lwp
struct_row['depends_on_payment_days'] = salary_component.depends_on_payment_days
struct_row['salary_component'] = salary_component.name
struct_row['abbr'] = salary_component.salary_component_abbr
struct_row['do_not_include_in_total'] = salary_component.do_not_include_in_total

View File

@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@ -14,10 +15,12 @@
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "activity_name",
"fieldtype": "Data",
"hidden": 0,
@ -45,10 +48,12 @@
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "user",
"fieldtype": "Link",
"hidden": 0,
@ -77,10 +82,12 @@
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "role",
"fieldtype": "Link",
"hidden": 0,
@ -109,10 +116,12 @@
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
@ -139,10 +148,12 @@
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "task",
"fieldtype": "Link",
"hidden": 0,
@ -171,11 +182,47 @@
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "1",
"fetch_if_empty": 0,
"fieldname": "task_weight",
"fieldtype": "Float",
"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": "Task Weight",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Applicable in the case of Employee Onboarding",
"fetch_if_empty": 0,
"fieldname": "required_for_employee_creation",
"fieldtype": "Check",
"hidden": 0,
@ -203,10 +250,12 @@
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "section_break_6",
"fieldtype": "Section Break",
"hidden": 0,
@ -233,10 +282,12 @@
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "description",
"fieldtype": "Text Editor",
"hidden": 0,
@ -264,16 +315,15 @@
}
],
"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-05-10 06:54:47.282492",
"menu_index": 0,
"modified": "2019-04-12 11:31:27.080747",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Boarding Activity",
@ -282,10 +332,10 @@
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
"track_seen": 0,
"track_views": 0
}

View File

@ -288,11 +288,7 @@ frappe.ui.form.on("Expense Claim Detail", {
claim_amount: function(frm, cdt, cdn) {
var child = locals[cdt][cdn];
var doc = frm.doc;
if(!child.sanctioned_amount){
frappe.model.set_value(cdt, cdn, 'sanctioned_amount', child.claim_amount);
}
cur_frm.cscript.calculate_total(doc,cdt,cdn);
},

View File

@ -513,7 +513,7 @@ def add_department_leaves(events, start, end, employee, company):
department_employees = frappe.db.sql_list("""select name from tabEmployee where department=%s
and company=%s""", (department, company))
filter_conditions = "employee in (\"%s\")" % '", "'.join(department_employees)
filter_conditions = " and employee in (\"%s\")" % '", "'.join(department_employees)
add_leaves(events, start, end, filter_conditions=filter_conditions)
def add_leaves(events, start, end, filter_conditions=None):

View File

@ -75,5 +75,5 @@ var set_value_for_condition_and_formula = function(frm) {
frm.set_value("amount_based_on_formula", 0);
frm.set_value("statistical_component", 0);
frm.set_value("do_not_include_in_total", 0);
frm.set_value("depends_on_lwp", 0);
frm.set_value("depends_on_payment_days", 0);
};

View File

@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
@ -19,6 +20,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "salary_component",
"fieldtype": "Data",
"hidden": 0,
@ -51,6 +53,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "salary_component_abbr",
"fieldtype": "Data",
"hidden": 0,
@ -85,6 +88,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "type",
"fieldtype": "Select",
"hidden": 0,
@ -119,6 +123,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.is_flexible_benefit != 1",
"fetch_if_empty": 0,
"fieldname": "is_additional_component",
"fieldtype": "Check",
"hidden": 0,
@ -153,6 +158,7 @@
"columns": 0,
"default": "1",
"depends_on": "eval:doc.type == \"Earning\"",
"fetch_if_empty": 0,
"fieldname": "is_tax_applicable",
"fieldtype": "Check",
"hidden": 0,
@ -186,6 +192,7 @@
"collapsible": 0,
"columns": 0,
"default": "1",
"fetch_if_empty": 0,
"fieldname": "is_payable",
"fieldtype": "Check",
"hidden": 0,
@ -218,7 +225,9 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "depends_on_lwp",
"default": "1",
"fetch_if_empty": 0,
"fieldname": "depends_on_payment_days",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
@ -227,7 +236,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Depends on Leave Without Pay",
"label": "Depends on Payment Days",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -250,6 +259,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "do_not_include_in_total",
"fieldtype": "Check",
"hidden": 0,
@ -282,6 +292,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_4",
"fieldtype": "Column Break",
"hidden": 0,
@ -313,6 +324,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "disabled",
"fieldtype": "Check",
"hidden": 0,
@ -345,6 +357,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "description",
"fieldtype": "Small Text",
"hidden": 0,
@ -378,6 +391,7 @@
"collapsible": 0,
"columns": 0,
"description": "If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ",
"fetch_if_empty": 0,
"fieldname": "statistical_component",
"fieldtype": "Check",
"hidden": 0,
@ -411,6 +425,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.type==\"Earning\" && doc.is_additional_component != 1 && doc.statistical_component!=1",
"fetch_if_empty": 0,
"fieldname": "flexible_benefits",
"fieldtype": "Section Break",
"hidden": 0,
@ -444,6 +459,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.is_additional_component != 1",
"fetch_if_empty": 0,
"fieldname": "is_flexible_benefit",
"fieldtype": "Check",
"hidden": 0,
@ -477,6 +493,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "is_flexible_benefit",
"fetch_if_empty": 0,
"fieldname": "max_benefit_amount",
"fieldtype": "Currency",
"hidden": 0,
@ -509,6 +526,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_9",
"fieldtype": "Column Break",
"hidden": 0,
@ -541,6 +559,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "is_flexible_benefit",
"fetch_if_empty": 0,
"fieldname": "pay_against_benefit_claim",
"fieldtype": "Check",
"hidden": 0,
@ -574,6 +593,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.is_flexible_benefit == 1 & doc.create_separate_payment_entry_against_benefit_claim !=1",
"fetch_if_empty": 0,
"fieldname": "only_tax_impact",
"fieldtype": "Check",
"hidden": 0,
@ -607,6 +627,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.is_flexible_benefit == 1 & doc.only_tax_impact !=1",
"fetch_if_empty": 0,
"fieldname": "create_separate_payment_entry_against_benefit_claim",
"fieldtype": "Check",
"hidden": 0,
@ -640,6 +661,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.type=='Deduction'",
"fetch_if_empty": 0,
"fieldname": "section_break_11",
"fieldtype": "Section Break",
"hidden": 0,
@ -671,6 +693,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "variable_based_on_taxable_salary",
"fieldtype": "Check",
"hidden": 0,
@ -704,6 +727,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.statistical_component != 1",
"fetch_if_empty": 0,
"fieldname": "section_break_5",
"fieldtype": "Section Break",
"hidden": 0,
@ -736,6 +760,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "accounts",
"fieldtype": "Table",
"hidden": 0,
@ -771,6 +796,7 @@
"collapsible_depends_on": "",
"columns": 0,
"depends_on": "eval:doc.is_flexible_benefit != 1 && doc.variable_based_on_taxable_salary != 1",
"fetch_if_empty": 0,
"fieldname": "condition_and_formula",
"fieldtype": "Section Break",
"hidden": 0,
@ -803,6 +829,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "condition",
"fieldtype": "Code",
"hidden": 0,
@ -836,6 +863,7 @@
"collapsible": 0,
"columns": 0,
"default": "",
"fetch_if_empty": 0,
"fieldname": "amount_based_on_formula",
"fieldtype": "Check",
"hidden": 0,
@ -869,6 +897,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.amount_based_on_formula!==0",
"fetch_if_empty": 0,
"fieldname": "formula",
"fieldtype": "Code",
"hidden": 0,
@ -902,6 +931,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.amount_based_on_formula!==1",
"fetch_if_empty": 0,
"fieldname": "amount",
"fieldtype": "Currency",
"hidden": 0,
@ -934,6 +964,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_28",
"fieldtype": "Column Break",
"hidden": 0,
@ -965,6 +996,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "help",
"fieldtype": "HTML",
"hidden": 0,
@ -1003,7 +1035,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-09-20 16:44:58.876044",
"modified": "2019-04-16 19:08:55.323567",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Component",

View File

@ -19,6 +19,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "salary_component",
"fieldtype": "Link",
"hidden": 0,
@ -55,6 +56,7 @@
"default": "",
"depends_on": "eval:doc.parenttype=='Salary Structure'",
"fetch_from": "salary_component.salary_component_abbr",
"fetch_if_empty": 0,
"fieldname": "abbr",
"fieldtype": "Data",
"hidden": 0,
@ -88,6 +90,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
@ -121,6 +124,7 @@
"columns": 0,
"description": "If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ",
"fetch_from": "salary_component.statistical_component",
"fetch_if_empty": 0,
"fieldname": "statistical_component",
"fieldtype": "Check",
"hidden": 0,
@ -154,6 +158,7 @@
"collapsible": 0,
"columns": 0,
"fetch_from": "salary_component.is_tax_applicable",
"fetch_if_empty": 0,
"fieldname": "is_tax_applicable",
"fieldtype": "Check",
"hidden": 0,
@ -187,6 +192,7 @@
"collapsible": 0,
"columns": 0,
"fetch_from": "salary_component.is_flexible_benefit",
"fetch_if_empty": 0,
"fieldname": "is_flexible_benefit",
"fieldtype": "Check",
"hidden": 0,
@ -221,6 +227,7 @@
"columns": 0,
"default": "",
"fetch_from": "salary_component.is_additional_component",
"fetch_if_empty": 0,
"fieldname": "is_additional_component",
"fieldtype": "Check",
"hidden": 1,
@ -255,6 +262,7 @@
"columns": 0,
"default": "",
"fetch_from": "salary_component.variable_based_on_taxable_salary",
"fetch_if_empty": 0,
"fieldname": "variable_based_on_taxable_salary",
"fieldtype": "Check",
"hidden": 0,
@ -288,8 +296,9 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_from": "salary_component.depends_on_lwp",
"fieldname": "depends_on_lwp",
"fetch_from": "salary_component.depends_on_payment_days",
"fetch_if_empty": 0,
"fieldname": "depends_on_payment_days",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
@ -298,7 +307,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Depends on Leave Without Pay",
"label": "Depends on Payment Days",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -322,6 +331,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.is_flexible_benefit != 1",
"fetch_if_empty": 0,
"fieldname": "section_break_2",
"fieldtype": "Section Break",
"hidden": 0,
@ -354,6 +364,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.parenttype=='Salary Structure'",
"fetch_if_empty": 0,
"fieldname": "condition",
"fieldtype": "Code",
"hidden": 0,
@ -389,6 +400,7 @@
"default": "0",
"depends_on": "eval:doc.parenttype=='Salary Structure'",
"fetch_from": "",
"fetch_if_empty": 0,
"fieldname": "amount_based_on_formula",
"fieldtype": "Check",
"hidden": 0,
@ -425,6 +437,7 @@
"default": "",
"depends_on": "eval:doc.amount_based_on_formula!==0 && doc.parenttype==='Salary Structure'",
"description": "",
"fetch_if_empty": 0,
"fieldname": "formula",
"fieldtype": "Code",
"hidden": 0,
@ -458,6 +471,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.amount_based_on_formula!==1 || doc.parenttype==='Salary Slip'",
"fetch_if_empty": 0,
"fieldname": "amount",
"fieldtype": "Currency",
"hidden": 0,
@ -491,6 +505,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "do_not_include_in_total",
"fieldtype": "Check",
"hidden": 0,
@ -524,6 +539,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.parenttype=='Salary Structure'",
"fetch_if_empty": 0,
"fieldname": "default_amount",
"fieldtype": "Currency",
"hidden": 0,
@ -558,6 +574,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.parenttype=='Salary Slip' && doc.parentfield=='deductions' && doc.variable_based_on_taxable_salary == 1",
"fetch_if_empty": 0,
"fieldname": "tax_on_flexible_benefit",
"fieldtype": "Currency",
"hidden": 0,
@ -591,6 +608,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.parenttype=='Salary Slip' && doc.parentfield=='deductions' && doc.variable_based_on_taxable_salary == 1",
"fetch_if_empty": 0,
"fieldname": "tax_on_additional_salary",
"fieldtype": "Currency",
"hidden": 0,
@ -624,6 +642,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.parenttype=='Salary Structure'",
"fetch_if_empty": 0,
"fieldname": "section_break_11",
"fieldtype": "Column Break",
"hidden": 0,
@ -656,6 +675,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.parenttype=='Salary Structure'",
"fetch_if_empty": 0,
"fieldname": "condition_and_formula_help",
"fieldtype": "HTML",
"hidden": 0,
@ -693,7 +713,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2019-02-04 14:41:56.030991",
"modified": "2019-04-16 19:09:31.726597",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Detail",

View File

@ -132,9 +132,6 @@ var get_emp_and_leave_details = function(doc, dt, dn) {
});
}
cur_frm.cscript.employee = function(doc,dt,dn){
get_emp_and_leave_details(doc, dt, dn);
}
cur_frm.cscript.leave_without_pay = function(doc,dt,dn){
if (doc.employee && doc.start_date && doc.end_date) {
@ -160,7 +157,7 @@ cur_frm.cscript.amount = function(doc,dt,dn){
calculate_all(doc, dt, dn);
}
cur_frm.cscript.depends_on_lwp = function(doc,dt,dn){
cur_frm.cscript.depends_on_payment_days = function(doc,dt,dn){
calculate_earning_total(doc, dt, dn, true);
calculate_ded_total(doc, dt, dn, true);
calculate_net_pay(doc, dt, dn);
@ -174,7 +171,7 @@ var calculate_earning_total = function(doc, dt, dn, reset_amount) {
var tbl = doc.earnings || [];
var total_earn = 0;
for(var i = 0; i < tbl.length; i++){
if(cint(tbl[i].depends_on_lwp) == 1) {
if(cint(tbl[i].depends_on_payment_days) == 1) {
tbl[i].amount = Math.round(tbl[i].default_amount)*(flt(doc.payment_days) /
cint(doc.total_working_days)*100)/100;
} else if(reset_amount && tbl[i].default_amount) {
@ -196,7 +193,7 @@ var calculate_ded_total = function(doc, dt, dn, reset_amount) {
var tbl = doc.deductions || [];
var total_ded = 0;
for(var i = 0; i < tbl.length; i++){
if(cint(tbl[i].depends_on_lwp) == 1) {
if(cint(tbl[i].depends_on_payment_days) == 1) {
tbl[i].amount = Math.round(tbl[i].default_amount)*(flt(doc.payment_days)/cint(doc.total_working_days)*100)/100;
} else if(reset_amount && tbl[i].default_amount) {
tbl[i].amount = tbl[i].default_amount;
@ -209,16 +206,12 @@ var calculate_ded_total = function(doc, dt, dn, reset_amount) {
refresh_many(['deductions', 'total_deduction']);
}
// Calculate net payable amount
// ------------------------------------------------------------------------
var calculate_net_pay = function(doc, dt, dn) {
doc.net_pay = flt(doc.gross_pay) - flt(doc.total_deduction);
doc.rounded_total = Math.round(doc.net_pay);
refresh_many(['net_pay', 'rounded_total']);
}
// validate
// ------------------------------------------------------------------------
cur_frm.cscript.validate = function(doc, dt, dn) {
calculate_all(doc, dt, dn);
}

View File

@ -131,11 +131,12 @@ class SalarySlip(TransactionBase):
for d in self.get(key):
if d.salary_component == struct_row.salary_component:
component_row = d
if not component_row:
self.append(key, {
'amount': amount,
'default_amount': amount,
'depends_on_lwp' : struct_row.depends_on_lwp,
'depends_on_payment_days' : struct_row.depends_on_payment_days,
'salary_component' : struct_row.salary_component,
'abbr' : struct_row.abbr,
'do_not_include_in_total' : struct_row.do_not_include_in_total,
@ -147,12 +148,11 @@ class SalarySlip(TransactionBase):
'tax_on_additional_salary': additional_tax
})
else:
if overwrite:
if not overwrite:
amount += struct_row.get("default_amount", 0)
component_row.default_amount = amount
component_row.amount = amount
else:
component_row.default_amount += amount
component_row.amount = component_row.default_amount
component_row.tax_on_flexible_benefit = benefit_tax
component_row.tax_on_additional_salary = additional_tax
@ -418,7 +418,7 @@ class SalarySlip(TransactionBase):
for d in self.get(component_type):
if (self.salary_structure and
cint(d.depends_on_lwp) and
cint(d.depends_on_payment_days) and
(not
self.salary_slip_based_on_timesheet or
getdate(self.start_date) < joining_date or
@ -431,7 +431,7 @@ class SalarySlip(TransactionBase):
)
elif not self.payment_days and not self.salary_slip_based_on_timesheet and \
cint(d.depends_on_lwp):
cint(d.depends_on_payment_days):
d.amount = 0
elif not d.amount:
d.amount = d.default_amount
@ -454,7 +454,7 @@ class SalarySlip(TransactionBase):
self.net_pay = 0
if self.total_working_days:
self.net_pay = (flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment))) * flt(self.payment_days / self.total_working_days)
self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment))
self.rounded_total = rounded(self.net_pay,
self.precision("net_pay") if disable_rounded_total else 0)
@ -787,7 +787,7 @@ class SalarySlip(TransactionBase):
component = frappe.get_doc("Salary Component", salary_component)
# Data for update_component_row
struct_row = {}
struct_row['depends_on_lwp'] = component.depends_on_lwp
struct_row['depends_on_payment_days'] = component.depends_on_payment_days
struct_row['salary_component'] = component.name
struct_row['abbr'] = component.salary_component_abbr
struct_row['do_not_include_in_total'] = component.do_not_include_in_total

View File

@ -246,7 +246,7 @@ frappe.ui.form.on('Salary Detail', {
frappe.model.set_value(cdt, cdn, 'amount', result.amount);
}
frappe.model.set_value(cdt, cdn, 'statistical_component', result.statistical_component);
frappe.model.set_value(cdt, cdn, 'depends_on_lwp', result.depends_on_lwp);
frappe.model.set_value(cdt, cdn, 'depends_on_payment_days', result.depends_on_payment_days);
frappe.model.set_value(cdt, cdn, 'do_not_include_in_total', result.do_not_include_in_total);
frappe.model.set_value(cdt, cdn, 'variable_based_on_taxable_salary', result.variable_based_on_taxable_salary);
frappe.model.set_value(cdt, cdn, 'is_tax_applicable', result.is_tax_applicable);

View File

@ -18,7 +18,7 @@ class SalaryStructure(Document):
self.validate_max_benefits_with_flexi()
def set_missing_values(self):
overwritten_fields = ["depends_on_lwp", "variable_based_on_taxable_salary", "is_tax_applicable", "is_flexible_benefit"]
overwritten_fields = ["depends_on_payment_days", "variable_based_on_taxable_salary", "is_tax_applicable", "is_flexible_benefit"]
overwritten_fields_if_missing = ["amount_based_on_formula", "formula", "amount"]
for table in ["earnings", "deductions"]:
for d in self.get(table):

View File

@ -189,7 +189,7 @@
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"options": "<h4>Condition Examples</h4>\n<ol>\n<li>Applying tax if employee born between 31-12-1937 and 01-01-1958 (Employees aged 60 to 80)<br>\n<code>Condition: date_of_birth&gt;date(1937, 12, 31) and date_of_birth&lt;date(1958, 01, 01)</code></li><br><li>Applying tax by employee gender<br>\n<code>Condition: gender=\"Male\"</code></li><br>\n<li>Applying tax by Salary Component<br>\n<code>Condition: base &gt; 10000</code></li></ol>",
"options": "<h4>Condition Examples</h4>\n<ol>\n<li>Applying tax if employee born between 31-12-1937 and 01-01-1958 (Employees aged 60 to 80)<br>\n<code>Condition: date_of_birth&gt;date(1937, 12, 31) and date_of_birth&lt;date(1958, 01, 01)</code></li><br><li>Applying tax by employee gender<br>\n<code>Condition: gender==\"Male\"</code></li><br>\n<li>Applying tax by Salary Component<br>\n<code>Condition: base &gt; 10000</code></li></ol>",
"permlevel": 0,
"precision": "",
"print_hide": 0,

View File

@ -15,9 +15,11 @@ class TrainingFeedback(Document):
def on_submit(self):
training_event = frappe.get_doc("Training Event", self.training_event)
status = None
for e in training_event.employees:
if e.employee == self.employee:
training_event.status = 'Feedback Submitted'
status = 'Feedback Submitted'
break
training_event.save()
if status:
frappe.db.set_value("Training Event", self.training_event, "status", status)

View File

@ -29,19 +29,12 @@ erpnext.hr.AttendanceControlPanel = frappe.ui.form.Controller.extend({
});
},
show_upload: function() {
var me = this;
show_upload() {
var $wrapper = $(cur_frm.fields_dict.upload_html.wrapper).empty();
// upload
frappe.upload.make({
parent: $wrapper,
args: {
method: 'erpnext.hr.doctype.upload_attendance.upload_attendance.upload'
},
no_socketio: true,
sample_url: "e.g. http://example.com/somefile.csv",
callback: function(attachment, r) {
new frappe.ui.FileUploader({
wrapper: $wrapper,
method: 'erpnext.hr.doctype.upload_attendance.upload_attendance.upload',
on_success(file_doc, r) {
var $log_wrapper = $(cur_frm.fields_dict.import_log.wrapper).empty();
if(!r.messages) r.messages = [];
@ -59,10 +52,10 @@ erpnext.hr.AttendanceControlPanel = frappe.ui.form.Controller.extend({
});
r.messages = ["<h4 style='color:red'>"+__("Import Failed!")+"</h4>"]
.concat(r.messages)
.concat(r.messages);
} else {
r.messages = ["<h4 style='color:green'>"+__("Import Successful!")+"</h4>"].
concat(r.message.messages)
r.messages = ["<h4 style='color:green'>"+__("Import Successful!")+"</h4>"]
.concat(r.message.messages);
}
$.each(r.messages, function(i, v) {
@ -79,11 +72,7 @@ erpnext.hr.AttendanceControlPanel = frappe.ui.form.Controller.extend({
});
}
});
// rename button
$wrapper.find('form input[type="submit"]')
.attr('value', 'Upload and Import')
}
},
})
cur_frm.cscript = new erpnext.hr.AttendanceControlPanel({frm: cur_frm});

View File

@ -116,10 +116,10 @@ def upload():
if not frappe.has_permission("Attendance", "create"):
raise frappe.PermissionError
from frappe.utils.csvutils import read_csv_content_from_uploaded_file
from frappe.utils.csvutils import read_csv_content
from frappe.modules import scrub
rows = read_csv_content_from_uploaded_file()
rows = read_csv_content(frappe.local.uploaded_file)
rows = list(filter(lambda x: x and any(x), rows))
if not rows:
msg = [_("Please select a csv file")]

View File

@ -6,7 +6,7 @@
"docstatus": 0,
"doctype": "Print Format",
"font": "Default",
"format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"HTML\", \"options\": \" <h3 style=\\\"text-align: right;\\\">{{doc.name}}</h3><div><hr></div> \"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"employee\"}, {\"print_hide\": 0, \"fieldname\": \"employee_name\"}, {\"print_hide\": 0, \"fieldname\": \"department\"}, {\"print_hide\": 0, \"fieldname\": \"designation\"}, {\"print_hide\": 0, \"fieldname\": \"branch\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"start_date\"}, {\"print_hide\": 0, \"fieldname\": \"end_date\"}, {\"print_hide\": 0, \"fieldname\": \"total_working_hours\"}, {\"print_hide\": 0, \"fieldname\": \"hour_rate\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"time_sheet\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"working_hours\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"timesheets\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"salary_component\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"depends_on_lwp\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"earnings\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"salary_component\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"depends_on_lwp\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"deductions\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"gross_pay\"}, {\"print_hide\": 0, \"fieldname\": \"total_deduction\"}, {\"print_hide\": 0, \"fieldname\": \"net_pay\"}, {\"print_hide\": 0, \"fieldname\": \"rounded_total\"}, {\"print_hide\": 0, \"fieldname\": \"total_in_words\"}]",
"format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"HTML\", \"options\": \" <h3 style=\\\"text-align: right;\\\">{{doc.name}}</h3><div><hr></div> \"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"employee\"}, {\"print_hide\": 0, \"fieldname\": \"employee_name\"}, {\"print_hide\": 0, \"fieldname\": \"department\"}, {\"print_hide\": 0, \"fieldname\": \"designation\"}, {\"print_hide\": 0, \"fieldname\": \"branch\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"start_date\"}, {\"print_hide\": 0, \"fieldname\": \"end_date\"}, {\"print_hide\": 0, \"fieldname\": \"total_working_hours\"}, {\"print_hide\": 0, \"fieldname\": \"hour_rate\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"time_sheet\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"working_hours\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"timesheets\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"salary_component\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"depends_on_payment_days\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"earnings\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"salary_component\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"depends_on_payment_days\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"deductions\"}, {\"fieldtype\": \"Section Break\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"gross_pay\"}, {\"print_hide\": 0, \"fieldname\": \"total_deduction\"}, {\"print_hide\": 0, \"fieldname\": \"net_pay\"}, {\"print_hide\": 0, \"fieldname\": \"rounded_total\"}, {\"print_hide\": 0, \"fieldname\": \"total_in_words\"}]",
"idx": 0,
"modified": "2016-08-21 21:02:59.896033",
"modified_by": "Administrator",

View File

@ -7,7 +7,7 @@
"docstatus": 0,
"doctype": "Print Format",
"font": "Default",
"format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \" <h3 style=\\\"text-align: right;\\\"><span style=\\\"line-height: 1.42857;\\\">{{doc.name}}</span></h3>\\n<div>\\n <hr style=\\\"text-align: center;\\\">\\n</div> \"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"employee\", \"label\": \"Employee\"}, {\"print_hide\": 0, \"fieldname\": \"company\", \"label\": \"Company\"}, {\"print_hide\": 0, \"fieldname\": \"employee_name\", \"label\": \"Employee Name\"}, {\"print_hide\": 0, \"fieldname\": \"department\", \"label\": \"Department\"}, {\"print_hide\": 0, \"fieldname\": \"designation\", \"label\": \"Designation\"}, {\"print_hide\": 0, \"fieldname\": \"branch\", \"label\": \"Branch\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"start_date\", \"label\": \"Start Date\"}, {\"print_hide\": 0, \"fieldname\": \"end_date\", \"label\": \"End Date\"}, {\"print_hide\": 0, \"fieldname\": \"total_working_days\", \"label\": \"Working Days\"}, {\"print_hide\": 0, \"fieldname\": \"leave_without_pay\", \"label\": \"Leave Without Pay\"}, {\"print_hide\": 0, \"fieldname\": \"payment_days\", \"label\": \"Payment Days\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"salary_component\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"earnings\", \"label\": \"Earnings\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"salary_component\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"depends_on_lwp\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"deductions\", \"label\": \"Deductions\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"gross_pay\", \"label\": \"Gross Pay\"}, {\"print_hide\": 0, \"fieldname\": \"total_deduction\", \"label\": \"Total Deduction\"}, {\"print_hide\": 0, \"fieldname\": \"net_pay\", \"label\": \"Net Pay\"}, {\"print_hide\": 0, \"fieldname\": \"rounded_total\", \"label\": \"Rounded Total\"}, {\"print_hide\": 0, \"fieldname\": \"total_in_words\", \"label\": \"Total in words\"}]",
"format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \" <h3 style=\\\"text-align: right;\\\"><span style=\\\"line-height: 1.42857;\\\">{{doc.name}}</span></h3>\\n<div>\\n <hr style=\\\"text-align: center;\\\">\\n</div> \"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"employee\", \"label\": \"Employee\"}, {\"print_hide\": 0, \"fieldname\": \"company\", \"label\": \"Company\"}, {\"print_hide\": 0, \"fieldname\": \"employee_name\", \"label\": \"Employee Name\"}, {\"print_hide\": 0, \"fieldname\": \"department\", \"label\": \"Department\"}, {\"print_hide\": 0, \"fieldname\": \"designation\", \"label\": \"Designation\"}, {\"print_hide\": 0, \"fieldname\": \"branch\", \"label\": \"Branch\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"start_date\", \"label\": \"Start Date\"}, {\"print_hide\": 0, \"fieldname\": \"end_date\", \"label\": \"End Date\"}, {\"print_hide\": 0, \"fieldname\": \"total_working_days\", \"label\": \"Working Days\"}, {\"print_hide\": 0, \"fieldname\": \"leave_without_pay\", \"label\": \"Leave Without Pay\"}, {\"print_hide\": 0, \"fieldname\": \"payment_days\", \"label\": \"Payment Days\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"salary_component\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"earnings\", \"label\": \"Earnings\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"salary_component\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"amount\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"depends_on_payment_days\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"deductions\", \"label\": \"Deductions\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"gross_pay\", \"label\": \"Gross Pay\"}, {\"print_hide\": 0, \"fieldname\": \"total_deduction\", \"label\": \"Total Deduction\"}, {\"print_hide\": 0, \"fieldname\": \"net_pay\", \"label\": \"Net Pay\"}, {\"print_hide\": 0, \"fieldname\": \"rounded_total\", \"label\": \"Rounded Total\"}, {\"print_hide\": 0, \"fieldname\": \"total_in_words\", \"label\": \"Total in words\"}]",
"idx": 0,
"line_breaks": 0,
"modified": "2018-07-24 19:31:39.040701",

View File

@ -45,7 +45,8 @@ class EmployeeBoardingController(Document):
"subject": activity.activity_name + " : " + self.employee_name,
"description": activity.description,
"department": self.department,
"company": self.company
"company": self.company,
"task_weight": activity.task_weight
}).insert(ignore_permissions=True)
activity.db_set("task", task.name)
users = [activity.user] if activity.user else []

View File

@ -620,7 +620,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite
is_stock_item=is_stock_item,
qty_field="stock_qty",
select_columns = """, bom_item.source_warehouse, bom_item.operation, bom_item.include_item_in_manufacturing,
(Select idx from `tabBOM Item` where item_code = bom_item.item_code and parent = %(parent)s ) as idx""")
(Select idx from `tabBOM Item` where item_code = bom_item.item_code and parent = %(parent)s limit 1) as idx""")
items = frappe.db.sql(query, { "parent": bom, "qty": qty, "bom": bom, "company": company }, as_dict=True)
elif fetch_scrap_items:
@ -682,6 +682,9 @@ def get_children(doctype, parent=None, is_root=False, **filters):
frappe.msgprint(_('Please select a BOM'))
return
if parent:
frappe.form_dict.parent = parent
if frappe.form_dict.parent:
bom_doc = frappe.get_doc("BOM", frappe.form_dict.parent)
frappe.has_permission("BOM", doc=bom_doc, throw=True)
@ -694,7 +697,7 @@ def get_children(doctype, parent=None, is_root=False, **filters):
item_names = tuple(d.get('item_code') for d in bom_items)
items = frappe.get_list('Item',
fields=['image', 'description', 'name'],
fields=['image', 'description', 'name', 'stock_uom', 'item_name'],
filters=[['name', 'in', item_names]]) # to get only required item dicts
for bom_item in bom_items:

View File

@ -20,13 +20,12 @@ class BOMUpdateTool(Document):
for bom in bom_list:
try:
bom_obj = frappe.get_doc("BOM", bom)
bom_obj.get_doc_before_save()
bom_obj.load_doc_before_save()
updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom)
bom_obj.calculate_cost()
bom_obj.update_parent_cost()
bom_obj.db_update()
if (getattr(bom_obj.meta, 'track_changes', False)
and bom_obj._doc_before_save and not bom_obj.flags.ignore_version):
if (getattr(bom_obj.meta, 'track_changes', False) and not bom_obj.flags.ignore_version):
bom_obj.save_version()
frappe.db.commit()

View File

@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@ -14,10 +15,12 @@
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "item_code",
"fieldtype": "Link",
"hidden": 0,
@ -41,14 +44,17 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "item_name",
"fieldtype": "Data",
"hidden": 0,
@ -72,14 +78,17 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "warehouse",
"fieldtype": "Link",
"hidden": 0,
@ -103,14 +112,51 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "material_request_type",
"fieldtype": "Select",
"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": "Material Request Type",
"length": 0,
"no_copy": 0,
"options": "\nPurchase\nMaterial Transfer\nMaterial Issue\nManufacture\nCustomer Provided",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_4",
"fieldtype": "Column Break",
"hidden": 0,
@ -132,14 +178,17 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "quantity",
"fieldtype": "Float",
"hidden": 0,
@ -149,7 +198,7 @@
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Quantity",
"label": "Required Quantity",
"length": 0,
"no_copy": 1,
"permlevel": 0,
@ -162,14 +211,52 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "projected_qty",
"fieldtype": "Float",
"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": "Projected Qty",
"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
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible_depends_on": "",
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "actual_qty",
"fieldtype": "Float",
"hidden": 0,
@ -192,14 +279,17 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "min_order_qty",
"fieldtype": "Float",
"hidden": 0,
@ -222,14 +312,17 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "section_break_8",
"fieldtype": "Section Break",
"hidden": 0,
@ -252,14 +345,17 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "sales_order",
"fieldtype": "Link",
"hidden": 0,
@ -283,14 +379,18 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "requested_qty",
"fieldtype": "Float",
"hidden": 0,
@ -313,20 +413,19 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"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-02-15 13:08:30.535963",
"modified": "2019-04-08 18:15:26.849602",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Material Request Plan Item",
@ -335,10 +434,10 @@
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
"track_seen": 0,
"track_views": 0
}

View File

@ -11,6 +11,23 @@ frappe.ui.form.on('Production Plan', {
}
}
frm.set_query('for_warehouse', function(doc) {
return {
filters: {
company: doc.company
}
}
});
frm.fields_dict['po_items'].grid.get_field('item_code').get_query = function(doc) {
return {
query: "erpnext.controllers.queries.item_query",
filters:{
'is_stock_item': 1,
}
}
}
frm.fields_dict['po_items'].grid.get_field('bom_no').get_query = function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
if (d.item_code) {
@ -50,6 +67,51 @@ frappe.ui.form.on('Production Plan', {
}
frm.trigger("material_requirement");
const projected_qty_formula = ` <table class="table table-bordered" style="background-color: #f9f9f9;">
<tr><td style="padding-left:25px">
<div>
<h3>
<a href = "https://erpnext.com/docs/user/manual/en/stock/projected-quantity">
${__("Projected Quantity Formula")}
</a>
</h3>
<div>
<h3 style="font-size: 13px">
(Actual Qty + Planned Qty + Requested Qty + Ordered Qty) - (Reserved Qty + Reserved for Production + Reserved for Subcontract)
</h3>
</div>
<br>
<div>
<ul>
<li>
${__("Actual Qty: Quantity available in the warehouse.")}
</li>
<li>
${__("Planned Qty: Quantity, for which, Work Order has been raised, but is pending to be manufactured.")}
</li>
<li>
${__('Requested Qty: Quantity requested for purchase, but not ordered.')}
</li>
<li>
${__('Ordered Qty: Quantity ordered for purchase, but not received.')}
</li>
<li>
${__("Reserved Qty: Quantity ordered for sale, but not delivered.")}
</li>
<li>
${__('Reserved Qty for Production: Raw materials quantity to make manufacturing items.')}
</li>
<li>
${__('Reserved Qty for Subcontract: Raw materials quantity to make subcotracted items.')}
</li>
</ul>
</div>
</div>
</td></tr>
</table>`;
set_field_options("projected_qty_formula", projected_qty_formula);
},
make_work_order: function(frm) {
@ -106,6 +168,8 @@ frappe.ui.form.on('Production Plan', {
},
get_items_for_mr: function(frm) {
const set_fields = ['actual_qty', 'item_code',
'item_name', 'min_order_qty', 'quantity', 'sales_order', 'warehouse', 'projected_qty', 'material_request_type'];
frappe.call({
method: "erpnext.manufacturing.doctype.production_plan.production_plan.get_items_for_material_requests",
freeze: true,
@ -115,13 +179,11 @@ frappe.ui.form.on('Production Plan', {
frm.set_value('mr_items', []);
$.each(r.message, function(i, d) {
var item = frm.add_child('mr_items');
item.actual_qty = d.actual_qty;
item.item_code = d.item_code;
item.item_name = d.item_name;
item.min_order_qty = d.min_order_qty;
item.quantity = d.quantity;
item.sales_order = d.sales_order;
item.warehouse = d.warehouse;
for (let key in d) {
if (d[key] && in_list(set_fields, key)) {
item[key] = d[key];
}
}
});
}
refresh_field('mr_items');
@ -129,6 +191,16 @@ frappe.ui.form.on('Production Plan', {
});
},
for_warehouse: function(frm) {
if (frm.doc.mr_items) {
frm.trigger("get_items_for_mr");
}
},
download_materials_required: function(frm) {
$c_obj_csv(frm.doc, 'download_raw_materials', '', '');
},
show_progress: function(frm) {
var bars = [];
var message = '';
@ -163,6 +235,25 @@ frappe.ui.form.on('Production Plan', {
},
});
frappe.ui.form.on("Production Plan Item", {
item_code: function(frm, cdt, cdn) {
const row = locals[cdt][cdn];
if (row.item_code) {
frappe.call({
method: "erpnext.manufacturing.doctype.production_plan.production_plan.get_item_data",
args: {
item_code: row.item_code
},
callback: function(r) {
for (let key in r.message) {
frappe.model.set_value(cdt, cdn, key, r.message[key]);
}
}
});
}
}
});
frappe.ui.form.on("Material Request Plan Item", {
warehouse: function(frm, cdt, cdn) {
const row = locals[cdt][cdn];
@ -170,12 +261,16 @@ frappe.ui.form.on("Material Request Plan Item", {
frappe.call({
method: "erpnext.manufacturing.doctype.production_plan.production_plan.get_bin_details",
args: {
row: row
row: row,
for_warehouse: row.warehouse
},
callback: function(r) {
frappe.model.set_value(cdt, cdn, 'actual_qty', r.message[1])
let {projected_qty, actual_qty} = r.message;
frappe.model.set_value(cdt, cdn, 'projected_qty', projected_qty);
frappe.model.set_value(cdt, cdn, 'actual_qty', actual_qty);
}
})
}
}
})
});

View File

@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@ -22,6 +23,7 @@
"collapsible": 0,
"columns": 0,
"default": "",
"fetch_if_empty": 0,
"fieldname": "naming_series",
"fieldtype": "Select",
"hidden": 0,
@ -56,6 +58,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
@ -90,6 +93,7 @@
"collapsible": 0,
"columns": 0,
"default": "",
"fetch_if_empty": 0,
"fieldname": "get_items_from",
"fieldtype": "Select",
"hidden": 0,
@ -123,6 +127,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break1",
"fieldtype": "Column Break",
"hidden": 0,
@ -155,6 +160,7 @@
"collapsible": 0,
"columns": 0,
"default": "Today",
"fetch_if_empty": 0,
"fieldname": "posting_date",
"fieldtype": "Date",
"hidden": 0,
@ -190,6 +196,7 @@
"columns": 0,
"depends_on": "eval: doc.get_items_from",
"description": "",
"fetch_if_empty": 0,
"fieldname": "filters",
"fieldtype": "Section Break",
"hidden": 0,
@ -222,6 +229,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "item_code",
"fieldtype": "Link",
"hidden": 0,
@ -256,6 +264,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval: doc.get_items_from == \"Sales Order\"",
"fetch_if_empty": 0,
"fieldname": "customer",
"fieldtype": "Link",
"hidden": 0,
@ -290,6 +299,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval: doc.get_items_from == \"Material Request\"",
"fetch_if_empty": 0,
"fieldname": "warehouse",
"fieldtype": "Link",
"hidden": 0,
@ -324,6 +334,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval: doc.get_items_from == \"Sales Order\"",
"fetch_if_empty": 0,
"fieldname": "project",
"fieldtype": "Link",
"hidden": 0,
@ -357,6 +368,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break2",
"fieldtype": "Column Break",
"hidden": 0,
@ -389,6 +401,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "from_date",
"fieldtype": "Date",
"hidden": 0,
@ -421,6 +434,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "to_date",
"fieldtype": "Date",
"hidden": 0,
@ -455,6 +469,7 @@
"collapsible_depends_on": "eval: doc.__islocal",
"columns": 0,
"depends_on": "eval: doc.get_items_from == \"Sales Order\"",
"fetch_if_empty": 0,
"fieldname": "sales_orders_detail",
"fieldtype": "Section Break",
"hidden": 0,
@ -489,6 +504,7 @@
"collapsible": 0,
"columns": 0,
"description": "",
"fetch_if_empty": 0,
"fieldname": "get_sales_orders",
"fieldtype": "Button",
"hidden": 0,
@ -522,6 +538,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "sales_orders",
"fieldtype": "Table",
"hidden": 0,
@ -557,6 +574,7 @@
"collapsible_depends_on": "eval: doc.__islocal",
"columns": 0,
"depends_on": "eval: doc.get_items_from == \"Material Request\"",
"fetch_if_empty": 0,
"fieldname": "material_request_detail",
"fieldtype": "Section Break",
"hidden": 0,
@ -590,6 +608,7 @@
"collapsible": 0,
"columns": 0,
"description": "",
"fetch_if_empty": 0,
"fieldname": "get_material_request",
"fieldtype": "Button",
"hidden": 0,
@ -623,6 +642,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "material_requests",
"fieldtype": "Table",
"hidden": 0,
@ -657,7 +677,8 @@
"collapsible": 0,
"columns": 0,
"description": "",
"fieldname": "items_for_production",
"fetch_if_empty": 0,
"fieldname": "select_items_to_manufacture_section",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
@ -666,7 +687,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Select Items",
"label": "Select Items to Manufacture",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -690,6 +711,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "get_items_from",
"fetch_if_empty": 0,
"fieldname": "get_items",
"fieldtype": "Button",
"hidden": 0,
@ -723,6 +745,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "po_items",
"fieldtype": "Table",
"hidden": 0,
@ -732,7 +755,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Items",
"label": "",
"length": 0,
"no_copy": 1,
"options": "Production Plan Item",
@ -757,6 +780,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "material_request_planning",
"fieldtype": "Section Break",
"hidden": 0,
@ -790,6 +814,7 @@
"collapsible": 0,
"columns": 0,
"default": "1",
"fetch_if_empty": 0,
"fieldname": "include_non_stock_items",
"fieldtype": "Check",
"hidden": 0,
@ -815,69 +840,6 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "ignore_existing_ordered_qty",
"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": "Ignore Existing Ordered Quantity",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_25",
"fieldtype": "Column Break",
"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,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@ -886,6 +848,7 @@
"collapsible": 0,
"columns": 0,
"default": "1",
"fetch_if_empty": 0,
"fieldname": "include_subcontracted_items",
"fieldtype": "Check",
"hidden": 0,
@ -918,9 +881,43 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fieldname": "section_break_27",
"fieldtype": "Section Break",
"description": "If enabled, then system will create the material even if the raw materials are available",
"fetch_if_empty": 0,
"fieldname": "ignore_existing_ordered_qty",
"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": "Ignore Existing Projected Quantity",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_25",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@ -950,6 +947,74 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "for_warehouse",
"fieldtype": "Link",
"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": "For Warehouse",
"length": 0,
"no_copy": 0,
"options": "Warehouse",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "download_materials_required",
"fieldtype": "Button",
"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": "Download Materials Required",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "get_items_for_mr",
"fieldtype": "Button",
"hidden": 0,
@ -982,6 +1047,40 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "section_break_27",
"fieldtype": "Section Break",
"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,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "mr_items",
"fieldtype": "Table",
"hidden": 0,
@ -1008,6 +1107,40 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fetch_if_empty": 0,
"fieldname": "projected_qty_formula",
"fieldtype": "HTML",
"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": "Projected Qty Formula",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@ -1015,6 +1148,7 @@
"bold": 0,
"collapsible": 1,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "other_details",
"fieldtype": "Section Break",
"hidden": 0,
@ -1048,6 +1182,7 @@
"collapsible": 0,
"columns": 0,
"default": "0",
"fetch_if_empty": 0,
"fieldname": "total_planned_qty",
"fieldtype": "Float",
"hidden": 0,
@ -1081,6 +1216,7 @@
"collapsible": 0,
"columns": 0,
"default": "0",
"fetch_if_empty": 0,
"fieldname": "total_produced_qty",
"fieldtype": "Float",
"hidden": 0,
@ -1113,6 +1249,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_32",
"fieldtype": "Column Break",
"hidden": 0,
@ -1145,6 +1282,7 @@
"collapsible": 0,
"columns": 0,
"default": "Draft",
"fetch_if_empty": 0,
"fieldname": "status",
"fieldtype": "Select",
"hidden": 0,
@ -1178,6 +1316,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 0,
@ -1205,17 +1344,15 @@
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-calendar",
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-08-21 14:44:25.071991",
"modified": "2019-04-09 12:05:14.300886",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Plan",
@ -1244,7 +1381,6 @@
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "ASC",

View File

@ -6,7 +6,7 @@ from __future__ import unicode_literals
import frappe, json
from frappe import msgprint, _
from frappe.model.document import Document
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_children
from frappe.utils import cstr, flt, cint, nowdate, add_days, comma_and, now_datetime, ceil
from erpnext.manufacturing.doctype.work_order.work_order import get_item_details
from six import string_types, iteritems
@ -262,7 +262,8 @@ class ProductionPlan(Document):
"fg_warehouse" : d.warehouse,
"production_plan" : self.name,
"production_plan_item" : d.name,
"product_bundle_item" : d.product_bundle_item
"product_bundle_item" : d.product_bundle_item,
"make_work_order_for_sub_assembly_items": d.get("make_work_order_for_sub_assembly_items", 0)
}
item_details.update({
@ -293,6 +294,10 @@ class ProductionPlan(Document):
if work_order:
wo_list.append(work_order)
if item.get("make_work_order_for_sub_assembly_items"):
work_orders = self.make_work_order_for_sub_assembly_items(item)
wo_list.extend(work_orders)
frappe.flags.mute_messages = False
if wo_list:
@ -302,11 +307,35 @@ class ProductionPlan(Document):
else :
msgprint(_("No Work Orders created"))
def make_work_order_for_sub_assembly_items(self, item):
work_orders = []
bom_data = {}
get_sub_assembly_items(item.get("bom_no"), bom_data)
for key, data in bom_data.items():
data.update({
'qty': data.get("stock_qty") * item.get("qty"),
'production_plan': self.name,
'company': self.company,
'fg_warehouse': item.get("fg_warehouse")
})
work_order = self.create_work_order(data)
if work_order:
work_orders.append(work_order)
return work_orders
def create_work_order(self, item):
from erpnext.manufacturing.doctype.work_order.work_order import OverProductionError, get_default_warehouse
warehouse = get_default_warehouse()
wo = frappe.new_doc("Work Order")
wo.update(item)
if item.get("warehouse"):
wo.fg_warehouse = item.get("warehouse")
wo.set_work_order_operations()
if not wo.fg_warehouse:
@ -325,8 +354,10 @@ class ProductionPlan(Document):
for item in self.mr_items:
item_doc = frappe.get_cached_doc('Item', item.item_code)
material_request_type = item.material_request_type or item_doc.default_material_request_type
# key for Sales Order:Material Request Type:Customer
key = '{}:{}:{}'.format(item.sales_order, item_doc.default_material_request_type,item_doc.customer or '')
key = '{}:{}:{}'.format(item.sales_order, material_request_type, item_doc.customer or '')
schedule_date = add_days(nowdate(), cint(item_doc.lead_time_days))
if not key in material_request_map:
@ -338,7 +369,7 @@ class ProductionPlan(Document):
"status": "Draft",
"company": self.company,
"requested_by": frappe.session.user,
'material_request_type': item_doc.default_material_request_type,
'material_request_type': material_request_type,
'customer': item_doc.customer or ''
})
material_request_list.append(material_request)
@ -373,6 +404,26 @@ class ProductionPlan(Document):
else :
msgprint(_("No material request created"))
def download_raw_materials(self):
item_list = [['Item Code', 'Description', 'Stock UOM', 'Required Qty', 'Warehouse',
'projected Qty', 'Actual Qty']]
doc = self.as_dict()
for d in get_items_for_material_requests(doc, ignore_existing_ordered_qty=True):
item_list.append([d.get('item_code'), d.get('description'), d.get('stock_uom'), d.get('quantity'),
d.get('warehouse'), d.get('projected_qty'), d.get('actual_qty')])
if not self.for_warehouse:
row = {'item_code': d.get('item_code')}
for bin_dict in get_bin_details(row, self.company, all_warehouse=True):
if d.get("warehouse") == bin_dict.get('warehouse'):
continue
item_list.append(['', '', '', '', bin_dict.get('warehouse'),
bin_dict.get('projected_qty'), bin_dict.get('actual_qty')])
return item_list
def get_exploded_items(item_details, company, bom_no, include_non_stock_items, planned_qty=1):
for d in frappe.db.sql("""select bei.item_code, item.default_bom as bom,
ifnull(sum(bei.stock_qty/ifnull(bom.quantity, 1)), 0)*%s as qty, item.item_name,
@ -439,17 +490,17 @@ def get_subitems(doc, data, item_details, bom_no, company, include_non_stock_ite
include_non_stock_items, include_subcontracted_items, d.qty)
return item_details
def get_material_request_items(row, sales_order, company, ignore_existing_ordered_qty, warehouse):
def get_material_request_items(row, sales_order,
company, ignore_existing_ordered_qty, warehouse, bin_dict):
total_qty = row['qty']
projected_qty, actual_qty = get_bin_details(row)
requested_qty = 0
if ignore_existing_ordered_qty:
requested_qty = total_qty
elif total_qty > projected_qty:
requested_qty = total_qty - projected_qty
if requested_qty > 0 and requested_qty < row['min_order_qty']:
requested_qty = row['min_order_qty']
required_qty = 0
if ignore_existing_ordered_qty or bin_dict.get("projected_qty") < 0:
required_qty = total_qty
elif total_qty > bin_dict.get("projected_qty"):
required_qty = total_qty - bin_dict.get("projected_qty")
if required_qty > 0 and required_qty < row['min_order_qty']:
required_qty = row['min_order_qty']
item_group_defaults = get_item_group_defaults(row.item_code, company)
if not row['purchase_uom']:
@ -459,20 +510,24 @@ def get_material_request_items(row, sales_order, company, ignore_existing_ordere
if not row['conversion_factor']:
frappe.throw(_("UOM Conversion factor ({0} -> {1}) not found for item: {2}")
.format(row['purchase_uom'], row['stock_uom'], row.item_code))
requested_qty = requested_qty / row['conversion_factor']
required_qty = required_qty / row['conversion_factor']
if frappe.db.get_value("UOM", row['purchase_uom'], "must_be_whole_number"):
requested_qty = ceil(requested_qty)
required_qty = ceil(required_qty)
if requested_qty > 0:
if required_qty > 0:
return {
'item_code': row.item_code,
'item_name': row.item_name,
'quantity': requested_qty,
'quantity': required_qty,
'description': row.description,
'stock_uom': row.get("stock_uom"),
'warehouse': warehouse or row.get('source_warehouse') \
or row.get('default_warehouse') or item_group_defaults.get("default_warehouse"),
'actual_qty': actual_qty,
'actual_qty': bin_dict.get("actual_qty", 0),
'projected_qty': bin_dict.get("projected_qty", 0),
'min_order_qty': row['min_order_qty'],
'material_request_type': row.get("default_material_request_type"),
'sales_order': sales_order
}
@ -515,37 +570,48 @@ def get_sales_orders(self):
return open_so
@frappe.whitelist()
def get_bin_details(row):
def get_bin_details(row, company, for_warehouse=None, all_warehouse=False):
if isinstance(row, string_types):
row = frappe._dict(json.loads(row))
conditions = ""
warehouse = row.get('source_warehouse') or row.get('default_warehouse')
company = frappe.db.escape(company)
conditions, warehouse = "", ""
conditions = " and warehouse in (select name from `tabWarehouse` where company = {0})".format(company)
if not all_warehouse:
warehouse = for_warehouse or row.get('source_warehouse') or row.get('default_warehouse')
if warehouse:
lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"])
conditions = " and exists(select name from `tabWarehouse` where lft >= {0} and rgt <= {1} and name=`tabBin`.warehouse)".format(lft, rgt)
conditions = """ and warehouse in (select name from `tabWarehouse`
where lft >= {0} and rgt <= {1} and name=`tabBin`.warehouse and company = {2})
""".format(lft, rgt, company)
item_projected_qty = frappe.db.sql(""" select ifnull(sum(projected_qty),0) as projected_qty,
ifnull(sum(actual_qty),0) as actual_qty from `tabBin`
return frappe.db.sql(""" select ifnull(sum(projected_qty),0) as projected_qty,
ifnull(sum(actual_qty),0) as actual_qty, warehouse from `tabBin`
where item_code = %(item_code)s {conditions}
""".format(conditions=conditions), { "item_code": row['item_code'] }, as_list=1)
return item_projected_qty and item_projected_qty[0] or (0,0)
group by item_code, warehouse
""".format(conditions=conditions), { "item_code": row['item_code'] }, as_dict=1)
@frappe.whitelist()
def get_items_for_material_requests(doc, sales_order=None, company=None):
def get_items_for_material_requests(doc, ignore_existing_ordered_qty=None):
if isinstance(doc, string_types):
doc = frappe._dict(json.loads(doc))
doc['mr_items'] = []
po_items = doc.get('po_items') if doc.get('po_items') else doc.get('items')
company = doc.get('company')
warehouse = doc.get('for_warehouse')
if not ignore_existing_ordered_qty:
ignore_existing_ordered_qty = doc.get('ignore_existing_ordered_qty')
so_item_details = frappe._dict()
for data in po_items:
warehouse = data.get('for_warehouse')
ignore_existing_ordered_qty = data.get('ignore_existing_ordered_qty') or doc.get('ignore_existing_ordered_qty')
planned_qty = data.get('required_qty') or data.get('planned_qty')
ignore_existing_ordered_qty = data.get('ignore_existing_ordered_qty') or ignore_existing_ordered_qty
warehouse = data.get("warehouse") or warehouse
item_details = {}
if data.get("bom") or data.get("bom_no"):
if data.get('required_qty'):
@ -592,7 +658,7 @@ def get_items_for_material_requests(doc, sales_order=None, company=None):
'conversion_factor' : conversion_factor,
}
)
if not sales_order:
sales_order = doc.get("sales_order")
for item_code, details in iteritems(item_details):
@ -606,10 +672,48 @@ def get_items_for_material_requests(doc, sales_order=None, company=None):
for sales_order, item_code in iteritems(so_item_details):
item_dict = so_item_details[sales_order]
for details in item_dict.values():
bin_dict = get_bin_details(details, doc.company, warehouse)
bin_dict = bin_dict[0] if bin_dict else {}
if details.qty > 0:
items = get_material_request_items(details, sales_order, company,
ignore_existing_ordered_qty, warehouse)
ignore_existing_ordered_qty, warehouse, bin_dict)
if items:
mr_items.append(items)
if not mr_items:
frappe.msgprint(_("""As raw materials projected quantity is more than required quantity, there is no need to create material request.
Still if you want to make material request, kindly enable <b>Ignore Existing Projected Quantity</b> checkbox"""))
return mr_items
@frappe.whitelist()
def get_item_data(item_code):
item_details = get_item_details(item_code)
return {
"bom_no": item_details.get("bom_no"),
"stock_uom": item_details.get("stock_uom"),
"description": item_details.get("description")
}
def get_sub_assembly_items(bom_no, bom_data):
data = get_children('BOM', parent = bom_no)
for d in data:
if d.expandable:
key = (d.name, d.value)
if key not in bom_data:
bom_data.setdefault(key, {
'stock_qty': 0,
'description': d.description,
'production_item': d.item_code,
'item_name': d.item_name,
'stock_uom': d.stock_uom,
'uom': d.stock_uom,
'bom_no': d.value
})
bom_item = bom_data.get(key)
bom_item["stock_qty"] += d.stock_qty
get_sub_assembly_items(bom_item.get("bom_no"), bom_data)

Some files were not shown because too many files have changed in this diff Show More