Merge branch 'develop' of https://github.com/frappe/erpnext into so-on-hold

This commit is contained in:
Mangesh-Khairnar 2019-04-09 12:13:45 +05:30
commit 16c4fd6733
140 changed files with 7457 additions and 5521 deletions

View File

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

View File

@ -0,0 +1,45 @@
frappe.dashboard_chart_sources["Account Balance Timeline"] = {
method_path: "erpnext.accounts.dashboard_chart_source.account_balance_timeline.account_balance_timeline.get",
filters: [
{
fieldname: "company",
label: __("Company"),
fieldtype: "Link",
options: "Company",
default: frappe.defaults.get_user_default("Company"),
reqd: 1
},
{
fieldname: "account",
label: __("Account"),
fieldtype: "Link",
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

@ -0,0 +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_by": "Administrator",
"module": "Accounts",
"name": "Account Balance Timeline",
"owner": "Administrator",
"source_name": "Account Balance Timeline"
}

View File

@ -0,0 +1,110 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# 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
from erpnext.accounts.report.general_ledger.general_ledger import execute
from frappe.utils.nestedset import get_descendants_of
@frappe.whitelist()
@cache_source
def get(filters=None):
timespan = filters.get("timespan")
timegrain = filters.get("timegrain")
account = filters.get("account")
company = filters.get("company")
from_date = get_from_date_from_timespan(timespan)
to_date = nowdate()
# 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)
# compile balance values
result = build_result(account, dates, gl_entries)
return {
"labels": [r[0].strftime('%Y-%m-%d') for r in result],
"datasets": [{
"name": account,
"values": [r[1] for r in result]
}]
}
def build_result(account, dates, gl_entries):
result = [[getdate(date), 0.0] for date in dates]
root_type = frappe.db.get_value('Account', account, 'root_type')
# start with the first date
date_index = 0
# get balances in debit
for entry in gl_entries:
# entry date is after the current pointer, so move the pointer forward
while getdate(entry.posting_date) > result[date_index][0]:
date_index += 1
result[date_index][1] += entry.debit - entry.credit
# if account type is credit, switch balances
if root_type not in ('Asset', 'Expense'):
for r in result:
r[1] = -1 * r[1]
# for balance sheet accounts, the totals are cumulative
if root_type in ('Asset', 'Liability', 'Equity'):
for i, r in enumerate(result):
if i < 0:
r[1] = r[1] + result[i-1][1]
return result
def get_gl_entries(account, to_date):
child_accounts = get_descendants_of('Account', account, ignore_permissions=True)
child_accounts.append(account)
return frappe.db.get_all('GL Entry',
fields = ['posting_date', 'debit', 'credit'],
filters = [
dict(posting_date = ('<', to_date)),
dict(account = ('in', child_accounts))
],
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:
days = 1
elif "Weekly" == timegrain:
days = 7
elif "Monthly" == timegrain:
months = 1
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))
return dates

View File

@ -2,9 +2,9 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import date_diff, add_months, today, getdate, add_days, flt from frappe.utils import date_diff, add_months, today, getdate, add_days, flt, get_last_day
from erpnext.accounts.utils import get_account_currency from erpnext.accounts.utils import get_account_currency
from erpnext.accounts.general_ledger import make_gl_entries from frappe.email import sendmail_to_system_managers
def validate_service_stop_date(doc): def validate_service_stop_date(doc):
''' Validates service_stop_date for Purchase Invoice and Sales Invoice ''' ''' Validates service_stop_date for Purchase Invoice and Sales Invoice '''
@ -33,47 +33,49 @@ def validate_service_stop_date(doc):
frappe.throw(_("Cannot change Service Stop Date for item in row {0}".format(item.idx))) frappe.throw(_("Cannot change Service Stop Date for item in row {0}".format(item.idx)))
def convert_deferred_expense_to_expense(start_date=None, end_date=None): def convert_deferred_expense_to_expense(start_date=None, end_date=None):
# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
if not start_date:
start_date = add_months(today(), -1)
if not end_date:
end_date = add_days(today(), -1)
# check for the purchase invoice for which GL entries has to be done # check for the purchase invoice for which GL entries has to be done
invoices = frappe.db.sql_list(''' invoices = frappe.db.sql_list('''
select distinct parent from `tabPurchase Invoice Item` where service_start_date<=%s and service_end_date>=%s select distinct parent from `tabPurchase Invoice Item`
where service_start_date<=%s and service_end_date>=%s
and enable_deferred_expense = 1 and docstatus = 1 and ifnull(amount, 0) > 0 and enable_deferred_expense = 1 and docstatus = 1 and ifnull(amount, 0) > 0
''', (end_date or today(), start_date or add_months(today(), -1))) ''', (end_date, start_date))
# For each invoice, book deferred expense # For each invoice, book deferred expense
for invoice in invoices: for invoice in invoices:
doc = frappe.get_doc("Purchase Invoice", invoice) doc = frappe.get_doc("Purchase Invoice", invoice)
book_deferred_income_or_expense(doc, start_date, end_date) book_deferred_income_or_expense(doc, end_date)
def convert_deferred_revenue_to_income(start_date=None, end_date=None): def convert_deferred_revenue_to_income(start_date=None, end_date=None):
# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
if not start_date:
start_date = add_months(today(), -1)
if not end_date:
end_date = add_days(today(), -1)
# check for the sales invoice for which GL entries has to be done # check for the sales invoice for which GL entries has to be done
invoices = frappe.db.sql_list(''' invoices = frappe.db.sql_list('''
select distinct parent from `tabSales Invoice Item` where service_start_date<=%s and service_end_date>=%s select distinct parent from `tabSales Invoice Item`
where service_start_date<=%s and service_end_date>=%s
and enable_deferred_revenue = 1 and docstatus = 1 and ifnull(amount, 0) > 0 and enable_deferred_revenue = 1 and docstatus = 1 and ifnull(amount, 0) > 0
''', (end_date or today(), start_date or add_months(today(), -1))) ''', (end_date, start_date))
# For each invoice, book deferred revenue
for invoice in invoices: for invoice in invoices:
doc = frappe.get_doc("Sales Invoice", invoice) doc = frappe.get_doc("Sales Invoice", invoice)
book_deferred_income_or_expense(doc, start_date, end_date) book_deferred_income_or_expense(doc, end_date)
def get_booking_dates(doc, item, posting_date=None):
if not posting_date:
posting_date = add_days(today(), -1)
last_gl_entry = False
def get_booking_dates(doc, item, start_date=None, end_date=None):
deferred_account = "deferred_revenue_account" if doc.doctype=="Sales Invoice" else "deferred_expense_account" deferred_account = "deferred_revenue_account" if doc.doctype=="Sales Invoice" else "deferred_expense_account"
last_gl_entry, skip = False, False
booking_end_date = getdate(add_days(today(), -1) if not end_date else end_date)
if booking_end_date < item.service_start_date or \
(item.service_stop_date and booking_end_date.month > item.service_stop_date.month):
return None, None, None, True
elif booking_end_date >= item.service_end_date:
last_gl_entry = True
booking_end_date = item.service_end_date
elif item.service_stop_date and item.service_stop_date <= booking_end_date:
last_gl_entry = True
booking_end_date = item.service_stop_date
booking_start_date = getdate(add_months(today(), -1) if not start_date else start_date)
booking_start_date = booking_start_date \
if booking_start_date > item.service_start_date else item.service_start_date
prev_gl_entry = frappe.db.sql(''' prev_gl_entry = frappe.db.sql('''
select name, posting_date from `tabGL Entry` where company=%s and account=%s and select name, posting_date from `tabGL Entry` where company=%s and account=%s and
@ -81,17 +83,28 @@ def get_booking_dates(doc, item, start_date=None, end_date=None):
order by posting_date desc limit 1 order by posting_date desc limit 1
''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True) ''', (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
if not prev_gl_entry and item.service_start_date < booking_start_date: if prev_gl_entry:
booking_start_date = item.service_start_date start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1))
elif prev_gl_entry: else:
booking_start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1)) start_date = item.service_start_date
skip = True if booking_start_date > booking_end_date else False
return last_gl_entry, booking_start_date, booking_end_date, skip end_date = get_last_day(start_date)
if end_date >= item.service_end_date:
end_date = item.service_end_date
last_gl_entry = True
elif item.service_stop_date and end_date >= item.service_stop_date:
end_date = item.service_stop_date
last_gl_entry = True
def calculate_amount_and_base_amount(doc, item, last_gl_entry, total_days, total_booking_days): if end_date > getdate(posting_date):
account_currency = get_account_currency(item.expense_account) end_date = posting_date
if getdate(start_date) <= getdate(end_date):
return start_date, end_date, last_gl_entry
else:
return None, None, None
def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, account_currency):
if doc.doctype == "Sales Invoice": if doc.doctype == "Sales Invoice":
total_credit_debit, total_credit_debit_currency = "debit", "debit_in_account_currency" total_credit_debit, total_credit_debit_currency = "debit", "debit_in_account_currency"
deferred_account = "deferred_revenue_account" deferred_account = "deferred_revenue_account"
@ -123,28 +136,15 @@ def calculate_amount_and_base_amount(doc, item, last_gl_entry, total_days, total
return amount, base_amount return amount, base_amount
def book_deferred_income_or_expense(doc, start_date=None, end_date=None): def book_deferred_income_or_expense(doc, posting_date=None):
# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
# start_date: 1st of the last month or the start date
# end_date: end_date or today-1
enable_check = "enable_deferred_revenue" \ enable_check = "enable_deferred_revenue" \
if doc.doctype=="Sales Invoice" else "enable_deferred_expense" if doc.doctype=="Sales Invoice" else "enable_deferred_expense"
gl_entries = [] def _book_deferred_revenue_or_expense(item):
for item in doc.get('items'): start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date)
if not item.get(enable_check): continue if not (start_date and end_date): return
skip = False
last_gl_entry, booking_start_date, booking_end_date, skip = \
get_booking_dates(doc, item, start_date, end_date)
if skip: continue
total_days = date_diff(item.service_end_date, item.service_start_date) + 1
total_booking_days = date_diff(booking_end_date, booking_start_date) + 1
account_currency = get_account_currency(item.expense_account) account_currency = get_account_currency(item.expense_account)
amount, base_amount = calculate_amount_and_base_amount(doc, item, last_gl_entry, total_days, total_booking_days)
if doc.doctype == "Sales Invoice": if doc.doctype == "Sales Invoice":
against, project = doc.customer, doc.project against, project = doc.customer, doc.project
credit_account, debit_account = item.income_account, item.deferred_revenue_account credit_account, debit_account = item.income_account, item.deferred_revenue_account
@ -152,36 +152,62 @@ def book_deferred_income_or_expense(doc, start_date=None, end_date=None):
against, project = doc.supplier, item.project against, project = doc.supplier, item.project
credit_account, debit_account = item.deferred_expense_account, item.expense_account credit_account, debit_account = item.deferred_expense_account, item.expense_account
# GL Entry for crediting the amount in the deferred expense total_days = date_diff(item.service_end_date, item.service_start_date) + 1
gl_entries.append( total_booking_days = date_diff(end_date, start_date) + 1
doc.get_gl_dict({
"account": credit_account, amount, base_amount = calculate_amount(doc, item, last_gl_entry,
"against": against, total_days, total_booking_days, account_currency)
"credit": base_amount,
"credit_in_account_currency": amount, make_gl_entries(doc, credit_account, debit_account, against,
"cost_center": item.cost_center, amount, base_amount, end_date, project, account_currency, item.cost_center, item.name)
"voucher_detail_no": item.name,
'posting_date': booking_end_date, if getdate(end_date) < getdate(posting_date) and not last_gl_entry:
'project': project _book_deferred_revenue_or_expense(item)
}, account_currency)
)
# GL Entry to debit the amount from the expense for item in doc.get('items'):
gl_entries.append( if item.get(enable_check):
doc.get_gl_dict({ _book_deferred_revenue_or_expense(item)
"account": debit_account,
"against": against, def make_gl_entries(doc, credit_account, debit_account, against,
"debit": base_amount, amount, base_amount, posting_date, project, account_currency, cost_center, voucher_detail_no):
"debit_in_account_currency": amount, # GL Entry for crediting the amount in the deferred expense
"cost_center": item.cost_center, from erpnext.accounts.general_ledger import make_gl_entries
"voucher_detail_no": item.name,
'posting_date': booking_end_date, gl_entries = []
'project': project gl_entries.append(
}, account_currency) doc.get_gl_dict({
) "account": credit_account,
"against": against,
"credit": base_amount,
"credit_in_account_currency": amount,
"cost_center": cost_center,
"voucher_detail_no": voucher_detail_no,
'posting_date': posting_date,
'project': project
}, account_currency)
)
# GL Entry to debit the amount from the expense
gl_entries.append(
doc.get_gl_dict({
"account": debit_account,
"against": against,
"debit": base_amount,
"debit_in_account_currency": amount,
"cost_center": cost_center,
"voucher_detail_no": voucher_detail_no,
'posting_date': posting_date,
'project': project
}, account_currency)
)
if gl_entries: if gl_entries:
try: try:
make_gl_entries(gl_entries, cancel=(doc.docstatus == 2), merge_entries=True) make_gl_entries(gl_entries, cancel=(doc.docstatus == 2), merge_entries=True)
frappe.db.commit() frappe.db.commit()
except: except:
frappe.db.rollback() frappe.db.rollback()
frappe.log_error(message = frappe.get_traceback(), title = _("Error while processing deferred accounting for {0}").format(doc.name)) title = _("Error while processing deferred accounting for {0}").format(doc.name)
traceback = frappe.get_traceback()
frappe.log_error(message=traceback , title=title)
sendmail_to_system_managers(title, traceback)

View File

@ -98,6 +98,8 @@ class Account(NestedSet):
ancestors = get_root_company(self.company) ancestors = get_root_company(self.company)
if ancestors: if ancestors:
if frappe.get_value("Company", self.company, "allow_account_creation_against_child_company"):
return
frappe.throw(_("Please add the account to root level Company - %s" % ancestors[0])) frappe.throw(_("Please add the account to root level Company - %s" % ancestors[0]))
else: else:
descendants = get_descendants_of('Company', self.company) descendants = get_descendants_of('Company', self.company)
@ -110,6 +112,8 @@ class Account(NestedSet):
["company", "name"], as_dict=True): ["company", "name"], as_dict=True):
acc_name_map[d["company"]] = d["name"] acc_name_map[d["company"]] = d["name"]
if not acc_name_map: return
for company in descendants: for company in descendants:
doc = frappe.copy_doc(self) doc = frappe.copy_doc(self)
doc.flags.ignore_root_company_validation = True doc.flags.ignore_root_company_validation = True

View File

@ -23,6 +23,10 @@ frappe.treeview_settings["Account"] = {
if(r.message) { if(r.message) {
let root_company = r.message.length ? r.message[0] : ""; let root_company = r.message.length ? r.message[0] : "";
me.page.fields_dict.root_company.set_value(root_company); me.page.fields_dict.root_company.set_value(root_company);
frappe.db.get_value("Company", {"name": company}, "allow_account_creation_against_child_company", (r) => {
frappe.flags.ignore_root_company_validation = r.allow_account_creation_against_child_company;
});
} }
} }
}); });
@ -133,9 +137,10 @@ frappe.treeview_settings["Account"] = {
{ {
label:__("Add Child"), label:__("Add Child"),
condition: function(node) { condition: function(node) {
return frappe.boot.user.can_create.indexOf("Account") !== -1 && return frappe.boot.user.can_create.indexOf("Account") !== -1
!frappe.treeview_settings['Account'].treeview.page.fields_dict.root_company.get_value() && && (!frappe.treeview_settings['Account'].treeview.page.fields_dict.root_company.get_value()
node.expandable && !node.hide_add; || frappe.flags.ignore_root_company_validation)
&& node.expandable && !node.hide_add;
}, },
click: function() { click: function() {
var me = frappe.treeview_settings['Account'].treeview; var me = frappe.treeview_settings['Account'].treeview;

View File

@ -146,7 +146,7 @@ def _make_test_records(verbose):
# related to Account Inventory Integration # related to Account Inventory Integration
["_Test Account Stock In Hand", "Current Assets", 0, None, None], ["_Test Account Stock In Hand", "Current Assets", 0, None, None],
# fixed asset depreciation # fixed asset depreciation
["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None], ["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None],
["_Test Accumulated Depreciations", "Current Assets", 0, None, None], ["_Test Accumulated Depreciations", "Current Assets", 0, None, None],
@ -183,13 +183,17 @@ def get_inventory_account(company, warehouse=None):
return account return account
def create_account(**kwargs): def create_account(**kwargs):
account = frappe.get_doc(dict( account = frappe.db.get_value("Account", filters={"account_name": kwargs.get("account_name"), "company": kwargs.get("company")})
doctype = "Account", if account:
account_name = kwargs.get('account_name'), return account
account_type = kwargs.get('account_type'), else:
parent_account = kwargs.get('parent_account'), account = frappe.get_doc(dict(
company = kwargs.get('company') doctype = "Account",
)) account_name = kwargs.get('account_name'),
account_type = kwargs.get('account_type'),
account.save() parent_account = kwargs.get('parent_account'),
return account.name company = kwargs.get('company')
))
account.save()
return account.name

View File

@ -23,6 +23,7 @@
"columns": 0, "columns": 0,
"default": "1", "default": "1",
"description": "If enabled, the system will post accounting entries for inventory automatically.", "description": "If enabled, the system will post accounting entries for inventory automatically.",
"fetch_if_empty": 0,
"fieldname": "auto_accounting_for_stock", "fieldname": "auto_accounting_for_stock",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 1, "hidden": 1,
@ -55,6 +56,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"description": "Accounting entry frozen up to this date, nobody can do / modify entry except role specified below.", "description": "Accounting entry frozen up to this date, nobody can do / modify entry except role specified below.",
"fetch_if_empty": 0,
"fieldname": "acc_frozen_upto", "fieldname": "acc_frozen_upto",
"fieldtype": "Date", "fieldtype": "Date",
"hidden": 0, "hidden": 0,
@ -87,6 +89,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"description": "Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts", "description": "Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts",
"fetch_if_empty": 0,
"fieldname": "frozen_accounts_modifier", "fieldname": "frozen_accounts_modifier",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -121,6 +124,7 @@
"columns": 0, "columns": 0,
"default": "Billing Address", "default": "Billing Address",
"description": "Address used to determine Tax Category in transactions.", "description": "Address used to determine Tax Category in transactions.",
"fetch_if_empty": 0,
"fieldname": "determine_address_tax_category_from", "fieldname": "determine_address_tax_category_from",
"fieldtype": "Select", "fieldtype": "Select",
"hidden": 0, "hidden": 0,
@ -154,6 +158,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_4", "fieldname": "column_break_4",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"hidden": 0, "hidden": 0,
@ -186,6 +191,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"description": "Role that is allowed to submit transactions that exceed credit limits set.", "description": "Role that is allowed to submit transactions that exceed credit limits set.",
"fetch_if_empty": 0,
"fieldname": "credit_controller", "fieldname": "credit_controller",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -218,6 +224,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "check_supplier_invoice_uniqueness", "fieldname": "check_supplier_invoice_uniqueness",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "hidden": 0,
@ -250,6 +257,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "make_payment_via_journal_entry", "fieldname": "make_payment_via_journal_entry",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "hidden": 0,
@ -283,6 +291,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"default": "1", "default": "1",
"fetch_if_empty": 0,
"fieldname": "unlink_payment_on_cancellation_of_invoice", "fieldname": "unlink_payment_on_cancellation_of_invoice",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "hidden": 0,
@ -316,6 +325,41 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"default": "1", "default": "1",
"fetch_if_empty": 0,
"fieldname": "unlink_advance_payment_on_cancelation_of_order",
"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": "Unlink Advance Payment on Cancelation of Order",
"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,
"default": "1",
"fetch_if_empty": 0,
"fieldname": "book_asset_depreciation_entry_automatically", "fieldname": "book_asset_depreciation_entry_automatically",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "hidden": 0,
@ -348,6 +392,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "allow_cost_center_in_entry_of_bs_account", "fieldname": "allow_cost_center_in_entry_of_bs_account",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "hidden": 0,
@ -381,6 +426,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"default": "1", "default": "1",
"fetch_if_empty": 0,
"fieldname": "add_taxes_from_item_tax_template", "fieldname": "add_taxes_from_item_tax_template",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "hidden": 0,
@ -413,6 +459,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "print_settings", "fieldname": "print_settings",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
@ -445,6 +492,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "show_inclusive_tax_in_print", "fieldname": "show_inclusive_tax_in_print",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "hidden": 0,
@ -477,6 +525,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_12", "fieldname": "column_break_12",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"hidden": 0, "hidden": 0,
@ -508,6 +557,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "show_payment_schedule_in_print", "fieldname": "show_payment_schedule_in_print",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "hidden": 0,
@ -540,6 +590,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "currency_exchange_section", "fieldname": "currency_exchange_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
@ -573,6 +624,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"default": "1", "default": "1",
"fetch_if_empty": 0,
"fieldname": "allow_stale", "fieldname": "allow_stale",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "hidden": 0,
@ -607,6 +659,7 @@
"columns": 0, "columns": 0,
"default": "1", "default": "1",
"depends_on": "eval:doc.allow_stale==0", "depends_on": "eval:doc.allow_stale==0",
"fetch_if_empty": 0,
"fieldname": "stale_days", "fieldname": "stale_days",
"fieldtype": "Int", "fieldtype": "Int",
"hidden": 0, "hidden": 0,
@ -639,6 +692,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "report_settings_sb", "fieldname": "report_settings_sb",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
@ -673,6 +727,7 @@
"columns": 0, "columns": 0,
"default": "0", "default": "0",
"description": "Only select if you have setup Cash Flow Mapper documents", "description": "Only select if you have setup Cash Flow Mapper documents",
"fetch_if_empty": 0,
"fieldname": "use_custom_cash_flow", "fieldname": "use_custom_cash_flow",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "hidden": 0,
@ -700,17 +755,15 @@
} }
], ],
"has_web_view": 0, "has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0, "hide_toolbar": 0,
"icon": "icon-cog", "icon": "icon-cog",
"idx": 1, "idx": 1,
"image_view": 0,
"in_create": 0, "in_create": 0,
"is_submittable": 0, "is_submittable": 0,
"issingle": 1, "issingle": 1,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2019-01-07 00:42:34.510150", "modified": "2019-04-06 12:28:43.026250",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Accounts Settings", "name": "Accounts Settings",
@ -776,10 +829,9 @@
], ],
"quick_entry": 1, "quick_entry": 1,
"read_only": 0, "read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0, "show_name_in_global_search": 0,
"sort_order": "ASC", "sort_order": "ASC",
"track_changes": 1, "track_changes": 1,
"track_seen": 0, "track_seen": 0,
"track_views": 0 "track_views": 0
} }

View File

@ -23,36 +23,36 @@ class BankReconciliation(Document):
journal_entries = frappe.db.sql(""" journal_entries = frappe.db.sql("""
select select
"Journal Entry" as payment_document, t1.name as payment_entry, "Journal Entry" as payment_document, t1.name as payment_entry,
t1.cheque_no as cheque_number, t1.cheque_date, t1.cheque_no as cheque_number, t1.cheque_date,
sum(t2.debit_in_account_currency) as debit, sum(t2.credit_in_account_currency) as credit, sum(t2.debit_in_account_currency) as debit, sum(t2.credit_in_account_currency) as credit,
t1.posting_date, t2.against_account, t1.clearance_date, t2.account_currency t1.posting_date, t2.against_account, t1.clearance_date, t2.account_currency
from from
`tabJournal Entry` t1, `tabJournal Entry Account` t2 `tabJournal Entry` t1, `tabJournal Entry Account` t2
where where
t2.parent = t1.name and t2.account = %s and t1.docstatus=1 t2.parent = t1.name and t2.account = %s and t1.docstatus=1
and t1.posting_date >= %s and t1.posting_date <= %s 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}
group by t2.account, t1.name group by t2.account, t1.name
order by t1.posting_date ASC, t1.name DESC 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), (self.bank_account, self.from_date, self.to_date), as_dict=1)
payment_entries = frappe.db.sql(""" payment_entries = frappe.db.sql("""
select select
"Payment Entry" as payment_document, name as payment_entry, "Payment Entry" as payment_document, name as payment_entry,
reference_no as cheque_number, reference_date as cheque_date, reference_no as cheque_number, reference_date as cheque_date,
if(paid_from=%(account)s, paid_amount, "") as credit, if(paid_from=%(account)s, paid_amount, 0) as credit,
if(paid_from=%(account)s, "", received_amount) as debit, if(paid_from=%(account)s, 0, received_amount) as debit,
posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date, posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date,
if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency
from `tabPayment Entry` from `tabPayment Entry`
where where
(paid_from=%(account)s or paid_to=%(account)s) and docstatus=1 (paid_from=%(account)s or paid_to=%(account)s) and docstatus=1
and posting_date >= %(from)s and posting_date <= %(to)s {0} and posting_date >= %(from)s and posting_date <= %(to)s {0}
order by order by
posting_date ASC, name DESC posting_date ASC, name DESC
""".format(condition), """.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}, as_dict=1)
pos_entries = [] pos_entries = []
@ -79,8 +79,12 @@ class BankReconciliation(Document):
for d in entries: for d in entries:
row = self.append('payment_entries', {}) row = self.append('payment_entries', {})
amount = d.debit if d.debit else d.credit
d.amount = fmt_money(amount, 2, d.account_currency) + " " + (_("Dr") if d.debit else _("Cr")) amount = flt(d.get('debit', 0)) - flt(d.get('credit', 0))
formatted_amount = fmt_money(abs(amount), 2, d.account_currency)
d.amount = formatted_amount + " " + (_("Dr") if amount > 0 else _("Cr"))
d.pop("credit") d.pop("credit")
d.pop("debit") d.pop("debit")
d.pop("account_currency") d.pop("account_currency")
@ -103,10 +107,10 @@ class BankReconciliation(Document):
d.clearance_date = None d.clearance_date = None
frappe.db.set_value(d.payment_document, d.payment_entry, "clearance_date", d.clearance_date) frappe.db.set_value(d.payment_document, d.payment_entry, "clearance_date", d.clearance_date)
frappe.db.sql("""update `tab{0}` set clearance_date = %s, modified = %s frappe.db.sql("""update `tab{0}` set clearance_date = %s, modified = %s
where name=%s""".format(d.payment_document), where name=%s""".format(d.payment_document),
(d.clearance_date, nowdate(), d.payment_entry)) (d.clearance_date, nowdate(), d.payment_entry))
clearance_date_updated = True clearance_date_updated = True
if clearance_date_updated: if clearance_date_updated:

View File

@ -52,11 +52,6 @@ class JournalEntry(AccountsController):
self.update_loan() self.update_loan()
self.update_inter_company_jv() self.update_inter_company_jv()
def before_print(self):
self.gl_entries = frappe.get_list("GL Entry",filters={"voucher_type": "Journal Entry",
"voucher_no": self.name} ,
fields=["account", "party_type", "party", "debit", "credit", "remarks"]
)
def get_title(self): def get_title(self):
return self.pay_to_recd_from or self.accounts[0].account return self.pay_to_recd_from or self.accounts[0].account

View File

@ -3,8 +3,8 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import flt
from frappe import _ from frappe import _
from frappe.utils import (flt, add_months)
from frappe.model.document import Document from frappe.model.document import Document
class MonthlyDistribution(Document): class MonthlyDistribution(Document):
@ -25,3 +25,33 @@ class MonthlyDistribution(Document):
if flt(total, 2) != 100.0: if flt(total, 2) != 100.0:
frappe.throw(_("Percentage Allocation should be equal to 100%") + \ frappe.throw(_("Percentage Allocation should be equal to 100%") + \
" ({0}%)".format(str(flt(total, 2)))) " ({0}%)".format(str(flt(total, 2))))
def get_periodwise_distribution_data(distribution_id, period_list, periodicity):
doc = frappe.get_doc('Monthly Distribution', distribution_id)
months_to_add = {
"Yearly": 12,
"Half-Yearly": 6,
"Quarterly": 3,
"Monthly": 1
}[periodicity]
period_dict = {}
for d in period_list:
period_dict[d.key] = get_percentage(doc, d.from_date, months_to_add)
return period_dict
def get_percentage(doc, start_date, period):
percentage = 0
months = [start_date.strftime("%B").title()]
for r in range(1, period):
months.append(add_months(start_date, r).strftime("%B").title())
for d in doc.percentages:
if d.month in months:
percentage += d.percentage_allocation
return percentage

View File

@ -70,11 +70,6 @@ class PaymentEntry(AccountsController):
self.update_advance_paid() self.update_advance_paid()
self.update_expense_claim() self.update_expense_claim()
def before_print(self):
self.gl_entries = frappe.get_list("GL Entry",filters={"voucher_type": "Payment Entry",
"voucher_no": self.name} ,
fields=["account", "party_type", "party", "debit", "credit", "remarks"]
)
def on_cancel(self): def on_cancel(self):
self.setup_party_account_field() self.setup_party_account_field()
@ -754,7 +749,7 @@ def get_outstanding_on_journal_entry(name):
@frappe.whitelist() @frappe.whitelist()
def get_reference_details(reference_doctype, reference_name, party_account_currency): def get_reference_details(reference_doctype, reference_name, party_account_currency):
total_amount = outstanding_amount = exchange_rate = None total_amount = outstanding_amount = exchange_rate = bill_no = None
ref_doc = frappe.get_doc(reference_doctype, reference_name) ref_doc = frappe.get_doc(reference_doctype, reference_name)
company_currency = ref_doc.get("company_currency") or erpnext.get_company_currency(ref_doc.company) company_currency = ref_doc.get("company_currency") or erpnext.get_company_currency(ref_doc.company)
@ -788,6 +783,7 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
if reference_doctype in ("Sales Invoice", "Purchase Invoice"): if reference_doctype in ("Sales Invoice", "Purchase Invoice"):
outstanding_amount = ref_doc.get("outstanding_amount") outstanding_amount = ref_doc.get("outstanding_amount")
bill_no = ref_doc.get("bill_no")
elif reference_doctype == "Expense Claim": elif reference_doctype == "Expense Claim":
outstanding_amount = flt(ref_doc.get("total_sanctioned_amount")) \ outstanding_amount = flt(ref_doc.get("total_sanctioned_amount")) \
- flt(ref_doc.get("total_amount+reimbursed")) - flt(ref_doc.get("total_advance_amount")) - flt(ref_doc.get("total_amount+reimbursed")) - flt(ref_doc.get("total_advance_amount"))
@ -804,7 +800,8 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
"due_date": ref_doc.get("due_date"), "due_date": ref_doc.get("due_date"),
"total_amount": total_amount, "total_amount": total_amount,
"outstanding_amount": outstanding_amount, "outstanding_amount": outstanding_amount,
"exchange_rate": exchange_rate "exchange_rate": exchange_rate,
"bill_no": bill_no
}) })

View File

@ -155,7 +155,6 @@ def pos_profile_query(doctype, txt, searchfield, start, page_len, filters):
def set_default_profile(pos_profile, company): def set_default_profile(pos_profile, company):
modified = now() modified = now()
user = frappe.session.user user = frappe.session.user
company = frappe.db.escape(company)
if pos_profile and company: if pos_profile and company:
frappe.db.sql(""" update `tabPOS Profile User` pfu, `tabPOS Profile` pf frappe.db.sql(""" update `tabPOS Profile User` pfu, `tabPOS Profile` pf

View File

@ -119,7 +119,7 @@ class PricingRule(Document):
#-------------------------------------------------------------------------------- #--------------------------------------------------------------------------------
@frappe.whitelist() @frappe.whitelist()
def apply_pricing_rule(args): def apply_pricing_rule(args, doc=None):
""" """
args = { args = {
"items": [{"doctype": "", "name": "", "item_code": "", "brand": "", "item_group": ""}, ...], "items": [{"doctype": "", "name": "", "item_code": "", "brand": "", "item_group": ""}, ...],
@ -139,6 +139,7 @@ def apply_pricing_rule(args):
"ignore_pricing_rule": "something" "ignore_pricing_rule": "something"
} }
""" """
if isinstance(args, string_types): if isinstance(args, string_types):
args = json.loads(args) args = json.loads(args)
@ -161,10 +162,11 @@ def apply_pricing_rule(args):
for item in item_list: for item in item_list:
args_copy = copy.deepcopy(args) args_copy = copy.deepcopy(args)
args_copy.update(item) args_copy.update(item)
data = get_pricing_rule_for_item(args_copy, item.get('price_list_rate')) data = get_pricing_rule_for_item(args_copy, item.get('price_list_rate'), doc=doc)
out.append(data) out.append(data)
if set_serial_nos_based_on_fifo and not args.get('is_return'): if not item.get("serial_no") and set_serial_nos_based_on_fifo and not args.get('is_return'):
out.append(get_serial_no_for_item(args_copy)) out[0].update(get_serial_no_for_item(args_copy))
return out return out
def get_serial_no_for_item(args): def get_serial_no_for_item(args):
@ -182,6 +184,12 @@ def get_serial_no_for_item(args):
def get_pricing_rule_for_item(args, price_list_rate=0, doc=None): def get_pricing_rule_for_item(args, price_list_rate=0, doc=None):
from erpnext.accounts.doctype.pricing_rule.utils import get_pricing_rules from erpnext.accounts.doctype.pricing_rule.utils import get_pricing_rules
if isinstance(doc, string_types):
doc = json.loads(doc)
if doc:
doc = frappe.get_doc(doc)
if (args.get('is_free_item') or if (args.get('is_free_item') or
args.get("parenttype") == "Material Request"): return {} args.get("parenttype") == "Material Request"): return {}
@ -227,11 +235,11 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None):
if pricing_rules: if pricing_rules:
rules = [] rules = []
item_details.discount_percentage = 0
item_details.discount_amount = 0
for pricing_rule in pricing_rules: for pricing_rule in pricing_rules:
if not pricing_rule or pricing_rule.get('suggestion'): continue if not pricing_rule or pricing_rule.get('suggestion'): continue
item_details.validate_applied_rule = pricing_rule.get("validate_applied_rule", 0)
rules.append(get_pricing_rule_details(args, pricing_rule)) rules.append(get_pricing_rule_details(args, pricing_rule))
if pricing_rule.mixed_conditions or pricing_rule.apply_rule_on_other: if pricing_rule.mixed_conditions or pricing_rule.apply_rule_on_other:
continue continue
@ -243,8 +251,8 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None):
item_details.has_pricing_rule = 1 item_details.has_pricing_rule = 1
# if discount is applied on the rate and not on price list rate # if discount is applied on the rate and not on price list rate
if price_list_rate: # if price_list_rate:
set_discount_amount(price_list_rate, item_details) # set_discount_amount(price_list_rate, item_details)
item_details.pricing_rules = ','.join([d.pricing_rule for d in rules]) item_details.pricing_rules = ','.join([d.pricing_rule for d in rules])
@ -264,7 +272,7 @@ def get_pricing_rule_details(args, pricing_rule):
'pricing_rule': pricing_rule.name, 'pricing_rule': pricing_rule.name,
'rate_or_discount': pricing_rule.rate_or_discount, 'rate_or_discount': pricing_rule.rate_or_discount,
'margin_type': pricing_rule.margin_type, 'margin_type': pricing_rule.margin_type,
'item_code': pricing_rule.item_code, 'item_code': pricing_rule.item_code or args.get("item_code"),
'child_docname': args.get('child_docname') 'child_docname': args.get('child_docname')
}) })
@ -296,6 +304,9 @@ def apply_price_discount_pricing_rule(pricing_rule, item_details, args):
discount_field = "{0}_on_rate".format(field) discount_field = "{0}_on_rate".format(field)
item_details[discount_field].append(pricing_rule.get(field, 0)) item_details[discount_field].append(pricing_rule.get(field, 0))
else: else:
if field not in item_details:
item_details.setdefault(field, 0)
item_details[field] += (pricing_rule.get(field, 0) item_details[field] += (pricing_rule.get(field, 0)
if pricing_rule else args.get(field, 0)) if pricing_rule else args.get(field, 0))
@ -308,6 +319,7 @@ def set_discount_amount(rate, item_details):
item_details.rate = rate item_details.rate = rate
def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None): def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None):
from erpnext.accounts.doctype.pricing_rule.utils import get_apply_on_and_items
for d in pricing_rules.split(','): for d in pricing_rules.split(','):
if not d: continue if not d: continue
pricing_rule = frappe.get_doc('Pricing Rule', d) pricing_rule = frappe.get_doc('Pricing Rule', d)
@ -327,6 +339,11 @@ def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None):
item_details.remove_free_item = (item_code if pricing_rule.get('same_item') item_details.remove_free_item = (item_code if pricing_rule.get('same_item')
else pricing_rule.get('free_item')) else pricing_rule.get('free_item'))
if pricing_rule.get("mixed_conditions") or pricing_rule.get("apply_rule_on_other"):
apply_on, items = get_apply_on_and_items(pricing_rule, item_details)
item_details.apply_on = apply_on
item_details.applied_on_items = ','.join(items)
item_details.pricing_rules = '' item_details.pricing_rules = ''
return item_details return item_details
@ -368,35 +385,6 @@ def make_pricing_rule(doctype, docname):
return doc return doc
@frappe.whitelist()
def get_free_items(pricing_rules, item_row):
if isinstance(item_row, string_types):
item_row = json.loads(item_row)
free_items = []
pricing_rules = list(set(pricing_rules.split(',')))
for d in pricing_rules:
pr_doc = frappe.get_doc('Pricing Rule', d)
if pr_doc.price_or_product_discount == 'Product':
item = (item_row.get('item_code') if pr_doc.same_item
else pr_doc.free_item)
if not item: return free_items
doc = frappe.get_doc('Item', item)
free_items.append({
'item_code': item,
'item_name': doc.item_name,
'description': doc.description,
'qty': pr_doc.free_qty,
'uom': pr_doc.free_item_uom,
'rate': pr_doc.free_item_rate or 0,
'is_free_item': 1
})
return free_items
def get_item_uoms(doctype, txt, searchfield, start, page_len, filters): def get_item_uoms(doctype, txt, searchfield, start, page_len, filters):
items = [filters.get('value')] items = [filters.get('value')]
if filters.get('apply_on') != 'Item Code': if filters.get('apply_on') != 'Item Code':

View File

@ -4,490 +4,531 @@
# For license information, please see license.txt # For license information, please see license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe, copy, json
import json
import copy
from frappe import throw, _ from frappe import throw, _
from six import string_types
from frappe.utils import flt, cint, get_datetime from frappe.utils import flt, cint, get_datetime
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
from erpnext.stock.get_item_details import get_conversion_factor from erpnext.stock.get_item_details import get_conversion_factor
class MultiplePricingRuleConflict(frappe.ValidationError): pass class MultiplePricingRuleConflict(frappe.ValidationError): pass
apply_on_table = {
'Item Code': 'items',
'Item Group': 'item_groups',
'Brand': 'brands'
}
def get_pricing_rules(args, doc=None): def get_pricing_rules(args, doc=None):
pricing_rules = [] pricing_rules = []
values = {} values = {}
for apply_on in ['Item Code', 'Item Group', 'Brand']: for apply_on in ['Item Code', 'Item Group', 'Brand']:
pricing_rules.extend(_get_pricing_rules(apply_on, args, values)) pricing_rules.extend(_get_pricing_rules(apply_on, args, values))
if pricing_rules and not apply_multiple_pricing_rules(pricing_rules): if pricing_rules and not apply_multiple_pricing_rules(pricing_rules):
break break
rules = [] rules = []
if not pricing_rules: return [] if not pricing_rules: return []
if apply_multiple_pricing_rules(pricing_rules): if apply_multiple_pricing_rules(pricing_rules):
for pricing_rule in pricing_rules: for pricing_rule in pricing_rules:
pricing_rule = filter_pricing_rules(args, pricing_rule, doc) pricing_rule = filter_pricing_rules(args, pricing_rule, doc)
if pricing_rule: if pricing_rule:
rules.append(pricing_rule) rules.append(pricing_rule)
else: else:
rules.append(filter_pricing_rules(args, pricing_rules, doc)) pricing_rule = filter_pricing_rules(args, pricing_rules, doc)
if pricing_rule:
rules.append(pricing_rule)
return rules return rules
def _get_pricing_rules(apply_on, args, values): def _get_pricing_rules(apply_on, args, values):
apply_on_field = frappe.scrub(apply_on) apply_on_field = frappe.scrub(apply_on)
if not args.get(apply_on_field): return [] if not args.get(apply_on_field): return []
child_doc = '`tabPricing Rule {0}`'.format(apply_on) child_doc = '`tabPricing Rule {0}`'.format(apply_on)
conditions = item_variant_condition = item_conditions = "" conditions = item_variant_condition = item_conditions = ""
values[apply_on_field] = args.get(apply_on_field) values[apply_on_field] = args.get(apply_on_field)
if apply_on_field in ['item_code', 'brand']: if apply_on_field in ['item_code', 'brand']:
item_conditions = "{child_doc}.{apply_on_field}= %({apply_on_field})s".format(child_doc=child_doc, item_conditions = "{child_doc}.{apply_on_field}= %({apply_on_field})s".format(child_doc=child_doc,
apply_on_field = apply_on_field) apply_on_field = apply_on_field)
if apply_on_field == 'item_code': if apply_on_field == 'item_code':
if "variant_of" not in args: if "variant_of" not in args:
args.variant_of = frappe.get_cached_value("Item", args.item_code, "variant_of") args.variant_of = frappe.get_cached_value("Item", args.item_code, "variant_of")
if args.variant_of: if args.variant_of:
item_variant_condition = ' or {child_doc}.item_code=%(variant_of)s '.format(child_doc=child_doc) item_variant_condition = ' or {child_doc}.item_code=%(variant_of)s '.format(child_doc=child_doc)
values['variant_of'] = args.variant_of values['variant_of'] = args.variant_of
elif apply_on_field == 'item_group': elif apply_on_field == 'item_group':
item_conditions = _get_tree_conditions(args, "Item Group", child_doc, False) item_conditions = _get_tree_conditions(args, "Item Group", child_doc, False)
conditions += get_other_conditions(conditions, values, args) conditions += get_other_conditions(conditions, values, args)
warehouse_conditions = _get_tree_conditions(args, "Warehouse", '`tabPricing Rule`') warehouse_conditions = _get_tree_conditions(args, "Warehouse", '`tabPricing Rule`')
if warehouse_conditions: if warehouse_conditions:
warehouse_conditions = " and {0}".format(warehouse_conditions) warehouse_conditions = " and {0}".format(warehouse_conditions)
if not args.price_list: args.price_list = None if not args.price_list: args.price_list = None
conditions += " and ifnull(`tabPricing Rule`.for_price_list, '') in (%(price_list)s, '')" conditions += " and ifnull(`tabPricing Rule`.for_price_list, '') in (%(price_list)s, '')"
values["price_list"] = args.get("price_list") values["price_list"] = args.get("price_list")
pricing_rules = frappe.db.sql("""select `tabPricing Rule`.*, pricing_rules = frappe.db.sql("""select `tabPricing Rule`.*,
{child_doc}.{apply_on_field}, {child_doc}.uom {child_doc}.{apply_on_field}, {child_doc}.uom
from `tabPricing Rule`, {child_doc} from `tabPricing Rule`, {child_doc}
where ({item_conditions} or (`tabPricing Rule`.apply_rule_on_other is not null where ({item_conditions} or (`tabPricing Rule`.apply_rule_on_other is not null
and `tabPricing Rule`.{apply_on_other_field}=%({apply_on_field})s) {item_variant_condition}) and `tabPricing Rule`.{apply_on_other_field}=%({apply_on_field})s) {item_variant_condition})
and {child_doc}.parent = `tabPricing Rule`.name and {child_doc}.parent = `tabPricing Rule`.name
and `tabPricing Rule`.disable = 0 and and `tabPricing Rule`.disable = 0 and
`tabPricing Rule`.{transaction_type} = 1 {warehouse_cond} {conditions} `tabPricing Rule`.{transaction_type} = 1 {warehouse_cond} {conditions}
order by `tabPricing Rule`.priority desc, order by `tabPricing Rule`.priority desc,
`tabPricing Rule`.name desc""".format( `tabPricing Rule`.name desc""".format(
child_doc = child_doc, child_doc = child_doc,
apply_on_field = apply_on_field, apply_on_field = apply_on_field,
item_conditions = item_conditions, item_conditions = item_conditions,
item_variant_condition = item_variant_condition, item_variant_condition = item_variant_condition,
transaction_type = args.transaction_type, transaction_type = args.transaction_type,
warehouse_cond = warehouse_conditions, warehouse_cond = warehouse_conditions,
apply_on_other_field = "other_{0}".format(apply_on_field), apply_on_other_field = "other_{0}".format(apply_on_field),
conditions = conditions), values, as_dict=1) or [] conditions = conditions), values, as_dict=1) or []
return pricing_rules return pricing_rules
def apply_multiple_pricing_rules(pricing_rules): def apply_multiple_pricing_rules(pricing_rules):
apply_multiple_rule = [d.apply_multiple_pricing_rules apply_multiple_rule = [d.apply_multiple_pricing_rules
for d in pricing_rules if d.apply_multiple_pricing_rules] for d in pricing_rules if d.apply_multiple_pricing_rules]
if not apply_multiple_rule: return False if not apply_multiple_rule: return False
if (apply_multiple_rule if (apply_multiple_rule
and len(apply_multiple_rule) == len(pricing_rules)): and len(apply_multiple_rule) == len(pricing_rules)):
return True return True
def _get_tree_conditions(args, parenttype, table, allow_blank=True): def _get_tree_conditions(args, parenttype, table, allow_blank=True):
field = frappe.scrub(parenttype) field = frappe.scrub(parenttype)
condition = "" condition = ""
if args.get(field): if args.get(field):
if not frappe.flags.tree_conditions: if not frappe.flags.tree_conditions:
frappe.flags.tree_conditions = {} frappe.flags.tree_conditions = {}
key = (parenttype, args.get(field)) key = (parenttype, args.get(field))
if key in frappe.flags.tree_conditions: if key in frappe.flags.tree_conditions:
return frappe.flags.tree_conditions[key] return frappe.flags.tree_conditions[key]
try: try:
lft, rgt = frappe.db.get_value(parenttype, args.get(field), ["lft", "rgt"]) lft, rgt = frappe.db.get_value(parenttype, args.get(field), ["lft", "rgt"])
except TypeError: except TypeError:
frappe.throw(_("Invalid {0}").format(args.get(field))) frappe.throw(_("Invalid {0}").format(args.get(field)))
parent_groups = frappe.db.sql_list("""select name from `tab%s` parent_groups = frappe.db.sql_list("""select name from `tab%s`
where lft<=%s and rgt>=%s""" % (parenttype, '%s', '%s'), (lft, rgt)) where lft<=%s and rgt>=%s""" % (parenttype, '%s', '%s'), (lft, rgt))
if parent_groups: if parent_groups:
if allow_blank: parent_groups.append('') if allow_blank: parent_groups.append('')
condition = "ifnull({table}.{field}, '') in ({parent_groups})".format( condition = "ifnull({table}.{field}, '') in ({parent_groups})".format(
table=table, table=table,
field=field, field=field,
parent_groups=", ".join([frappe.db.escape(d) for d in parent_groups]) parent_groups=", ".join([frappe.db.escape(d) for d in parent_groups])
) )
frappe.flags.tree_conditions[key] = condition frappe.flags.tree_conditions[key] = condition
return condition return condition
def get_other_conditions(conditions, values, args): def get_other_conditions(conditions, values, args):
for field in ["company", "customer", "supplier", "campaign", "sales_partner"]: for field in ["company", "customer", "supplier", "campaign", "sales_partner"]:
if args.get(field): if args.get(field):
conditions += " and ifnull(`tabPricing Rule`.{0}, '') in (%({1})s, '')".format(field, field) conditions += " and ifnull(`tabPricing Rule`.{0}, '') in (%({1})s, '')".format(field, field)
values[field] = args.get(field) values[field] = args.get(field)
else: else:
conditions += " and ifnull(`tabPricing Rule`.{0}, '') = ''".format(field) conditions += " and ifnull(`tabPricing Rule`.{0}, '') = ''".format(field)
for parenttype in ["Customer Group", "Territory", "Supplier Group"]: for parenttype in ["Customer Group", "Territory", "Supplier Group"]:
group_condition = _get_tree_conditions(args, parenttype, '`tabPricing Rule`') group_condition = _get_tree_conditions(args, parenttype, '`tabPricing Rule`')
if group_condition: if group_condition:
conditions += " and " + group_condition conditions += " and " + group_condition
if args.get("transaction_date"): if args.get("transaction_date"):
conditions += """ and %(transaction_date)s between ifnull(`tabPricing Rule`.valid_from, '2000-01-01') conditions += """ and %(transaction_date)s between ifnull(`tabPricing Rule`.valid_from, '2000-01-01')
and ifnull(`tabPricing Rule`.valid_upto, '2500-12-31')""" and ifnull(`tabPricing Rule`.valid_upto, '2500-12-31')"""
values['transaction_date'] = args.get('transaction_date') values['transaction_date'] = args.get('transaction_date')
return conditions return conditions
def filter_pricing_rules(args, pricing_rules, doc=None): def filter_pricing_rules(args, pricing_rules, doc=None):
if not isinstance(pricing_rules, list): if not isinstance(pricing_rules, list):
pricing_rules = [pricing_rules] pricing_rules = [pricing_rules]
original_pricing_rule = copy.copy(pricing_rules) original_pricing_rule = copy.copy(pricing_rules)
# filter for qty # filter for qty
if pricing_rules: if pricing_rules:
stock_qty = flt(args.get('stock_qty')) stock_qty = flt(args.get('stock_qty'))
amount = flt(args.get('price_list_rate')) * flt(args.get('qty')) amount = flt(args.get('price_list_rate')) * flt(args.get('qty'))
if pricing_rules[0].apply_rule_on_other: if pricing_rules[0].apply_rule_on_other:
field = frappe.scrub(pricing_rules[0].apply_rule_on_other) field = frappe.scrub(pricing_rules[0].apply_rule_on_other)
if (field and pricing_rules[0].get('other_' + field) != args.get(field)): return if (field and pricing_rules[0].get('other_' + field) != args.get(field)): return
pr_doc = frappe.get_doc('Pricing Rule', pricing_rules[0].name) pr_doc = frappe.get_doc('Pricing Rule', pricing_rules[0].name)
if pricing_rules[0].mixed_conditions and doc: if pricing_rules[0].mixed_conditions and doc:
stock_qty, amount = get_qty_and_rate_for_mixed_conditions(doc, pr_doc) stock_qty, amount = get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args)
elif pricing_rules[0].is_cumulative: elif pricing_rules[0].is_cumulative:
items = [args.get(frappe.scrub(pr_doc.get('apply_on')))] items = [args.get(frappe.scrub(pr_doc.get('apply_on')))]
data = get_qty_amount_data_for_cumulative(pr_doc, args, items) data = get_qty_amount_data_for_cumulative(pr_doc, args, items)
if data: if data:
stock_qty += data[0] stock_qty += data[0]
amount += data[1] amount += data[1]
if pricing_rules[0].apply_rule_on_other and not pricing_rules[0].mixed_conditions and doc: if pricing_rules[0].apply_rule_on_other and not pricing_rules[0].mixed_conditions and doc:
pricing_rules = get_qty_and_rate_for_other_item(doc, pr_doc, pricing_rules) or [] pricing_rules = get_qty_and_rate_for_other_item(doc, pr_doc, pricing_rules) or []
else: else:
pricing_rules = filter_pricing_rules_for_qty_amount(stock_qty, amount, pricing_rules, args) pricing_rules = filter_pricing_rules_for_qty_amount(stock_qty, amount, pricing_rules, args)
if not pricing_rules: if not pricing_rules:
for d in original_pricing_rule: for d in original_pricing_rule:
if not d.threshold_percentage: continue if not d.threshold_percentage: continue
msg = validate_quantity_and_amount_for_suggestion(d, stock_qty, msg = validate_quantity_and_amount_for_suggestion(d, stock_qty,
amount, args.get('item_code'), args.get('transaction_type')) amount, args.get('item_code'), args.get('transaction_type'))
if msg: if msg:
return {'suggestion': msg, 'item_code': args.get('item_code')} return {'suggestion': msg, 'item_code': args.get('item_code')}
# add variant_of property in pricing rule # add variant_of property in pricing rule
for p in pricing_rules: for p in pricing_rules:
if p.item_code and args.variant_of: if p.item_code and args.variant_of:
p.variant_of = args.variant_of p.variant_of = args.variant_of
else: else:
p.variant_of = None p.variant_of = None
# find pricing rule with highest priority # find pricing rule with highest priority
if pricing_rules: if pricing_rules:
max_priority = max([cint(p.priority) for p in pricing_rules]) max_priority = max([cint(p.priority) for p in pricing_rules])
if max_priority: if max_priority:
pricing_rules = list(filter(lambda x: cint(x.priority)==max_priority, pricing_rules)) pricing_rules = list(filter(lambda x: cint(x.priority)==max_priority, pricing_rules))
# apply internal priority # apply internal priority
all_fields = ["item_code", "item_group", "brand", "customer", "customer_group", "territory", all_fields = ["item_code", "item_group", "brand", "customer", "customer_group", "territory",
"supplier", "supplier_group", "campaign", "sales_partner", "variant_of"] "supplier", "supplier_group", "campaign", "sales_partner", "variant_of"]
if len(pricing_rules) > 1: if len(pricing_rules) > 1:
for field_set in [["item_code", "variant_of", "item_group", "brand"], for field_set in [["item_code", "variant_of", "item_group", "brand"],
["customer", "customer_group", "territory"], ["supplier", "supplier_group"]]: ["customer", "customer_group", "territory"], ["supplier", "supplier_group"]]:
remaining_fields = list(set(all_fields) - set(field_set)) remaining_fields = list(set(all_fields) - set(field_set))
if if_all_rules_same(pricing_rules, remaining_fields): if if_all_rules_same(pricing_rules, remaining_fields):
pricing_rules = apply_internal_priority(pricing_rules, field_set, args) pricing_rules = apply_internal_priority(pricing_rules, field_set, args)
break break
if pricing_rules and not isinstance(pricing_rules, list): if pricing_rules and not isinstance(pricing_rules, list):
pricing_rules = list(pricing_rules) pricing_rules = list(pricing_rules)
if len(pricing_rules) > 1: if len(pricing_rules) > 1:
rate_or_discount = list(set([d.rate_or_discount for d in pricing_rules])) rate_or_discount = list(set([d.rate_or_discount for d in pricing_rules]))
if len(rate_or_discount) == 1 and rate_or_discount[0] == "Discount Percentage": if len(rate_or_discount) == 1 and rate_or_discount[0] == "Discount Percentage":
pricing_rules = filter(lambda x: x.for_price_list==args.price_list, pricing_rules) \ pricing_rules = filter(lambda x: x.for_price_list==args.price_list, pricing_rules) \
or pricing_rules or pricing_rules
if len(pricing_rules) > 1 and not args.for_shopping_cart: if len(pricing_rules) > 1 and not args.for_shopping_cart:
frappe.throw(_("Multiple Price Rules exists with same criteria, please resolve conflict by assigning priority. Price Rules: {0}") frappe.throw(_("Multiple Price Rules exists with same criteria, please resolve conflict by assigning priority. Price Rules: {0}")
.format("\n".join([d.name for d in pricing_rules])), MultiplePricingRuleConflict) .format("\n".join([d.name for d in pricing_rules])), MultiplePricingRuleConflict)
elif pricing_rules: elif pricing_rules:
return pricing_rules[0] return pricing_rules[0]
def validate_quantity_and_amount_for_suggestion(args, qty, amount, item_code, transaction_type): def validate_quantity_and_amount_for_suggestion(args, qty, amount, item_code, transaction_type):
fieldname, msg = '', '' fieldname, msg = '', ''
type_of_transaction = 'purcahse' if transaction_type == "buying" else "sale" type_of_transaction = 'purcahse' if transaction_type == "buying" else "sale"
for field, value in {'min_qty': qty, 'min_amt': amount}.items(): for field, value in {'min_qty': qty, 'min_amt': amount}.items():
if (args.get(field) and value < args.get(field) if (args.get(field) and value < args.get(field)
and (args.get(field) - cint(args.get(field) * args.threshold_percentage * 0.01)) <= value): and (args.get(field) - cint(args.get(field) * args.threshold_percentage * 0.01)) <= value):
fieldname = field fieldname = field
for field, value in {'max_qty': qty, 'max_amt': amount}.items(): for field, value in {'max_qty': qty, 'max_amt': amount}.items():
if (args.get(field) and value > args.get(field) if (args.get(field) and value > args.get(field)
and (args.get(field) + cint(args.get(field) * args.threshold_percentage * 0.01)) >= value): and (args.get(field) + cint(args.get(field) * args.threshold_percentage * 0.01)) >= value):
fieldname = field fieldname = field
if fieldname: if fieldname:
msg = _("""If you {0} {1} quantities of the item <b>{2}</b>, the scheme <b>{3}</b> msg = _("""If you {0} {1} quantities of the item <b>{2}</b>, the scheme <b>{3}</b>
will be applied on the item.""").format(type_of_transaction, args.get(fieldname), item_code, args.rule_description) will be applied on the item.""").format(type_of_transaction, args.get(fieldname), item_code, args.rule_description)
if fieldname in ['min_amt', 'max_amt']: if fieldname in ['min_amt', 'max_amt']:
msg = _("""If you {0} {1} worth item <b>{2}</b>, the scheme <b>{3}</b> will be applied on the item. msg = _("""If you {0} {1} worth item <b>{2}</b>, the scheme <b>{3}</b> will be applied on the item.
""").format(frappe.fmt_money(type_of_transaction, args.get(fieldname)), item_code, args.rule_description) """).format(frappe.fmt_money(type_of_transaction, args.get(fieldname)), item_code, args.rule_description)
frappe.msgprint(msg) frappe.msgprint(msg)
return msg return msg
def filter_pricing_rules_for_qty_amount(qty, rate, pricing_rules, args=None): def filter_pricing_rules_for_qty_amount(qty, rate, pricing_rules, args=None):
rules = [] rules = []
for rule in pricing_rules: for rule in pricing_rules:
status = False status = False
conversion_factor = 1 conversion_factor = 1
if rule.get("uom"): if rule.get("uom"):
conversion_factor = get_conversion_factor(rule.item_code, rule.uom).get("conversion_factor", 1) conversion_factor = get_conversion_factor(rule.item_code, rule.uom).get("conversion_factor", 1)
if (flt(qty) >= (flt(rule.min_qty) * conversion_factor) if (flt(qty) >= (flt(rule.min_qty) * conversion_factor)
and (flt(qty)<= (rule.max_qty * conversion_factor) if rule.max_qty else True)): and (flt(qty)<= (rule.max_qty * conversion_factor) if rule.max_qty else True)):
status = True status = True
# if user has created item price against the transaction UOM # if user has created item price against the transaction UOM
if rule.get("uom") == args.get("uom"): if rule.get("uom") == args.get("uom"):
conversion_factor = 1.0 conversion_factor = 1.0
if status and (flt(rate) >= (flt(rule.min_amt) * conversion_factor) if status and (flt(rate) >= (flt(rule.min_amt) * conversion_factor)
and (flt(rate)<= (rule.max_amt * conversion_factor) if rule.max_amt else True)): and (flt(rate)<= (rule.max_amt * conversion_factor) if rule.max_amt else True)):
status = True status = True
else: else:
status = False status = False
if status: if status:
rules.append(rule) rules.append(rule)
return rules return rules
def if_all_rules_same(pricing_rules, fields): def if_all_rules_same(pricing_rules, fields):
all_rules_same = True all_rules_same = True
val = [pricing_rules[0].get(k) for k in fields] val = [pricing_rules[0].get(k) for k in fields]
for p in pricing_rules[1:]: for p in pricing_rules[1:]:
if val != [p.get(k) for k in fields]: if val != [p.get(k) for k in fields]:
all_rules_same = False all_rules_same = False
break break
return all_rules_same return all_rules_same
def apply_internal_priority(pricing_rules, field_set, args): def apply_internal_priority(pricing_rules, field_set, args):
filtered_rules = [] filtered_rules = []
for field in field_set: for field in field_set:
if args.get(field): if args.get(field):
filtered_rules = filter(lambda x: x[field]==args[field], pricing_rules) filtered_rules = filter(lambda x: x[field]==args[field], pricing_rules)
if filtered_rules: break if filtered_rules: break
return filtered_rules or pricing_rules return filtered_rules or pricing_rules
def get_qty_and_rate_for_mixed_conditions(doc, pr_doc): def get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args):
sum_qty, sum_amt = [0, 0] sum_qty, sum_amt = [0, 0]
items = get_pricing_rule_items(pr_doc) or [] items = get_pricing_rule_items(pr_doc) or []
apply_on = frappe.scrub(pr_doc.get('apply_on')) apply_on = frappe.scrub(pr_doc.get('apply_on'))
if items and doc.get("items"): if items and doc.get("items"):
for row in doc.get('items'): for row in doc.get('items'):
if row.get(apply_on) not in items: continue if row.get(apply_on) not in items: continue
if pr_doc.mixed_conditions: if pr_doc.mixed_conditions:
sum_qty += row.stock_qty amt = args.get('qty') * args.get("price_list_rate")
sum_amt += row.amount if args.get("item_code") != row.get("item_code"):
amt = row.get('qty') * row.get("price_list_rate")
if pr_doc.is_cumulative: sum_qty += row.get("stock_qty") or args.get("stock_qty")
data = get_qty_amount_data_for_cumulative(pr_doc, doc, items) sum_amt += amt
if data and data[0]: if pr_doc.is_cumulative:
sum_qty += data[0] data = get_qty_amount_data_for_cumulative(pr_doc, doc, items)
sum_amt += data[1]
return sum_qty, sum_amt if data and data[0]:
sum_qty += data[0]
sum_amt += data[1]
return sum_qty, sum_amt
def get_qty_and_rate_for_other_item(doc, pr_doc, pricing_rules): def get_qty_and_rate_for_other_item(doc, pr_doc, pricing_rules):
for d in get_pricing_rule_items(pr_doc): for d in get_pricing_rule_items(pr_doc):
for row in doc.items: for row in doc.items:
if d == row.get(frappe.scrub(pr_doc.apply_on)): if d == row.get(frappe.scrub(pr_doc.apply_on)):
pricing_rules = filter_pricing_rules_for_qty_amount(row.stock_qty, pricing_rules = filter_pricing_rules_for_qty_amount(row.get("stock_qty"),
row.amount, pricing_rules, row) row.get("amount"), pricing_rules, row)
if pricing_rules and pricing_rules[0]: if pricing_rules and pricing_rules[0]:
return pricing_rules return pricing_rules
def get_qty_amount_data_for_cumulative(pr_doc, doc, items=[]): def get_qty_amount_data_for_cumulative(pr_doc, doc, items=[]):
sum_qty, sum_amt = [0, 0] sum_qty, sum_amt = [0, 0]
doctype = doc.get('parenttype') or doc.doctype doctype = doc.get('parenttype') or doc.doctype
date_field = ('transaction_date' date_field = ('transaction_date'
if doc.get('transaction_date') else 'posting_date') if doc.get('transaction_date') else 'posting_date')
child_doctype = '{0} Item'.format(doctype) child_doctype = '{0} Item'.format(doctype)
apply_on = frappe.scrub(pr_doc.get('apply_on')) apply_on = frappe.scrub(pr_doc.get('apply_on'))
values = [pr_doc.valid_from, pr_doc.valid_upto] values = [pr_doc.valid_from, pr_doc.valid_upto]
condition = "" condition = ""
if pr_doc.warehouse: if pr_doc.warehouse:
warehouses = get_child_warehouses(pr_doc.warehouse) warehouses = get_child_warehouses(pr_doc.warehouse)
condition += """ and `tab{child_doc}`.warehouse in ({warehouses}) condition += """ and `tab{child_doc}`.warehouse in ({warehouses})
""".format(child_doc=child_doctype, warehouses = ','.join(['%s'] * len(warehouses))) """.format(child_doc=child_doctype, warehouses = ','.join(['%s'] * len(warehouses)))
values.extend(warehouses) values.extend(warehouses)
if items: if items:
condition = " and `tab{child_doc}`.{apply_on} in ({items})".format(child_doc = child_doctype, condition = " and `tab{child_doc}`.{apply_on} in ({items})".format(child_doc = child_doctype,
apply_on = apply_on, items = ','.join(['%s'] * len(items))) apply_on = apply_on, items = ','.join(['%s'] * len(items)))
values.extend(items) values.extend(items)
data_set = frappe.db.sql(""" SELECT `tab{child_doc}`.stock_qty, data_set = frappe.db.sql(""" SELECT `tab{child_doc}`.stock_qty,
`tab{child_doc}`.amount `tab{child_doc}`.amount
FROM `tab{child_doc}`, `tab{parent_doc}` FROM `tab{child_doc}`, `tab{parent_doc}`
WHERE WHERE
`tab{child_doc}`.parent = `tab{parent_doc}`.name and {date_field} `tab{child_doc}`.parent = `tab{parent_doc}`.name and {date_field}
between %s and %s and `tab{parent_doc}`.docstatus = 1 between %s and %s and `tab{parent_doc}`.docstatus = 1
{condition} group by `tab{child_doc}`.name {condition} group by `tab{child_doc}`.name
""".format(parent_doc = doctype, """.format(parent_doc = doctype,
child_doc = child_doctype, child_doc = child_doctype,
condition = condition, condition = condition,
date_field = date_field date_field = date_field
), tuple(values), as_dict=1) ), tuple(values), as_dict=1)
for data in data_set: for data in data_set:
sum_qty += data.get('stock_qty') sum_qty += data.get('stock_qty')
sum_amt += data.get('amount') sum_amt += data.get('amount')
return [sum_qty, sum_amt] return [sum_qty, sum_amt]
def validate_pricing_rules(doc): def validate_pricing_rules(doc):
validate_pricing_rule_on_transactions(doc) validate_pricing_rule_on_transactions(doc)
if not doc.pricing_rules: return for d in doc.items:
validate_pricing_rule_on_items(doc, d)
for d in doc.items: doc.calculate_taxes_and_totals()
validate_pricing_rule_on_items(doc, d)
doc.calculate_taxes_and_totals() def validate_pricing_rule_on_items(doc, item_row, do_not_validate = False):
value = 0
for pricing_rule in get_applied_pricing_rules(doc, item_row):
pr_doc = frappe.get_doc('Pricing Rule', pricing_rule)
def validate_pricing_rule_on_items(doc, item_row): if pr_doc.get('apply_on') == 'Transaction': continue
value = 0
for pr_row in get_applied_pricing_rules(doc, item_row):
pr_doc = frappe.get_doc('Pricing Rule', pr_row.pricing_rule)
if pr_doc.get('apply_on') == 'Transaction': continue if pr_doc.get('price_or_product_discount') == 'Product':
apply_pricing_rule_for_free_items(doc, pr_doc)
else:
for field in ['discount_percentage', 'discount_amount', 'rate']:
if not pr_doc.get(field): continue
if pr_doc.get('price_or_product_discount') == 'Product': value += pr_doc.get(field)
apply_pricing_rule_for_free_items(doc, pr_doc) apply_pricing_rule(doc, pr_doc, item_row, value, do_not_validate)
else:
for field in ['discount_percentage', 'discount_amount', 'rate']:
if not pr_doc.get(field): continue
value += pr_doc.get(field)
apply_pricing_rule(doc, pr_doc, pr_row, item_row, value)
def validate_pricing_rule_on_transactions(doc): def validate_pricing_rule_on_transactions(doc):
conditions = "apply_on = 'Transaction'" conditions = "apply_on = 'Transaction'"
values = {} values = {}
conditions = get_other_conditions(conditions, values, doc) conditions = get_other_conditions(conditions, values, doc)
pricing_rules = frappe.db.sql(""" Select `tabPricing Rule`.* from `tabPricing Rule` pricing_rules = frappe.db.sql(""" Select `tabPricing Rule`.* from `tabPricing Rule`
where {conditions} """.format(conditions = conditions), values, as_dict=1) where {conditions} """.format(conditions = conditions), values, as_dict=1)
if pricing_rules: if pricing_rules:
pricing_rules = filter_pricing_rules_for_qty_amount(doc.total_qty, pricing_rules = filter_pricing_rules_for_qty_amount(doc.total_qty,
doc.total, pricing_rules) doc.total, pricing_rules)
for d in pricing_rules: for d in pricing_rules:
if d.price_or_product_discount == 'Price': if d.price_or_product_discount == 'Price':
if d.apply_discount_on: if d.apply_discount_on:
doc.set('apply_discount_on', d.apply_discount_on) doc.set('apply_discount_on', d.apply_discount_on)
for field in ['additional_discount_percentage', 'discount_amount']: for field in ['additional_discount_percentage', 'discount_amount']:
if not d.get(field): continue if not d.get(field): continue
pr_field = ('discount_percentage' pr_field = ('discount_percentage'
if field == 'additional_discount_percentage' else field) if field == 'additional_discount_percentage' else field)
if d.validate_applied_rule and doc.get(field) < d.get(pr_field): if d.validate_applied_rule and doc.get(field) < d.get(pr_field):
frappe.msgprint(_("User has not applied rule on the invoice {0}") frappe.msgprint(_("User has not applied rule on the invoice {0}")
.format(doc.name)) .format(doc.name))
else: else:
doc.set(field, d.get(pr_field)) doc.set(field, d.get(pr_field))
elif d.price_or_product_discount == 'Product': elif d.price_or_product_discount == 'Product':
apply_pricing_rule_for_free_items(doc, d) apply_pricing_rule_for_free_items(doc, d)
def get_applied_pricing_rules(doc, item_row): def get_applied_pricing_rules(doc, item_row):
return [d for d in doc.pricing_rules return (item_row.get("pricing_rules").split(',')
if d.child_docname == item_row.name] if item_row.get("pricing_rules") else [])
def apply_pricing_rule_for_free_items(doc, pricing_rule): def apply_pricing_rule_for_free_items(doc, pricing_rule):
if pricing_rule.get('free_item'): if pricing_rule.get('free_item'):
items = [d.item_code for d in doc.items items = [d.item_code for d in doc.items
if d.item_code == (d.item_code if d.item_code == (d.item_code
if pricing_rule.get('same_item') else pricing_rule.get('free_item')) and d.is_free_item] if pricing_rule.get('same_item') else pricing_rule.get('free_item')) and d.is_free_item]
if not items: if not items:
doc.append('items', { doc.append('items', {
'item_code': pricing_rule.get('free_item'), 'item_code': pricing_rule.get('free_item'),
'qty': pricing_rule.get('free_qty'), 'qty': pricing_rule.get('free_qty'),
'uom': pricing_rule.get('free_item_uom'), 'uom': pricing_rule.get('free_item_uom'),
'rate': pricing_rule.get('free_item_rate'), 'rate': pricing_rule.get('free_item_rate'),
'is_free_item': 1 'is_free_item': 1
}) })
doc.set_missing_values() doc.set_missing_values()
def apply_pricing_rule(doc, pr_doc, pr_row, item_row, value): def apply_pricing_rule(doc, pr_doc, item_row, value, do_not_validate=False):
apply_on = frappe.scrub(pr_doc.get('apply_on')) apply_on, items = get_apply_on_and_items(pr_doc, item_row)
items = (get_pricing_rule_items(pr_doc)
if pr_doc.mixed_conditions else [item_row.get(apply_on)])
if pr_doc.apply_rule_on_other: rule_applied = {}
apply_on = frappe.scrub(pr_doc.apply_rule_on_other)
items = [pr_doc.get(apply_on)]
rule_applied = 1 for item in doc.get("items"):
if item_row.get(apply_on) in items: if not item.pricing_rules:
for field in ['discount_percentage', 'discount_amount', 'rate']: item.pricing_rules = item_row.pricing_rules
if not pr_doc.get(field): continue
if not pr_doc.validate_applied_rule: if item.get(apply_on) in items:
item_row.set(field, value) for field in ['discount_percentage', 'discount_amount', 'rate']:
elif item_row.get(field) < value: if not pr_doc.get(field): continue
rule_applied = 0
frappe.msgprint(_("Row {0}: user has not applied rule <b>{1}</b> on the item <b>{2}</b>")
.format(item_row.idx, pr_doc.title, item_row.item_code))
pr_row.rule_applied = rule_applied key = (item.name, item.pricing_rules)
if not pr_doc.validate_applied_rule:
rule_applied[key] = 1
item.set(field, value)
elif item.get(field) < value:
if not do_not_validate and item.idx == item_row.idx:
rule_applied[key] = 0
frappe.msgprint(_("Row {0}: user has not applied rule <b>{1}</b> on the item <b>{2}</b>")
.format(item.idx, pr_doc.title, item.item_code))
if rule_applied and doc.get("pricing_rules"):
for d in doc.get("pricing_rules"):
key = (d.child_docname, d.pricing_rule)
if key in rule_applied:
d.rule_applied = 1
def get_apply_on_and_items(pr_doc, item_row):
# for mixed or other items conditions
apply_on = frappe.scrub(pr_doc.get('apply_on'))
items = (get_pricing_rule_items(pr_doc)
if pr_doc.mixed_conditions else [item_row.get(apply_on)])
if pr_doc.apply_rule_on_other:
apply_on = frappe.scrub(pr_doc.apply_rule_on_other)
items = [pr_doc.get(apply_on)]
return apply_on, items
def get_pricing_rule_items(pr_doc): def get_pricing_rule_items(pr_doc):
apply_on = frappe.scrub(pr_doc.get('apply_on')) apply_on = frappe.scrub(pr_doc.get('apply_on'))
return [item.get(apply_on) for item in pr_doc.items] or []
pricing_rule_apply_on = apply_on_table.get(pr_doc.get('apply_on'))
return [item.get(apply_on) for item in pr_doc.get(pricing_rule_apply_on)] or []
@frappe.whitelist()
def validate_pricing_rule_for_different_cond(doc):
if isinstance(doc, string_types):
doc = json.loads(doc)
doc = frappe.get_doc(doc)
for d in doc.get("items"):
validate_pricing_rule_on_items(doc, d, True)
return doc

View File

@ -468,7 +468,7 @@ cur_frm.fields_dict["items"].grid.get_field("cost_center").get_query = function(
cur_frm.cscript.cost_center = function(doc, cdt, cdn){ cur_frm.cscript.cost_center = function(doc, cdt, cdn){
var d = locals[cdt][cdn]; var d = locals[cdt][cdn];
if(d.idx == 1 && d.cost_center){ if(d.cost_center){
var cl = doc.items || []; var cl = doc.items || [];
for(var i = 0; i < cl.length; i++){ for(var i = 0; i < cl.length; i++){
if(!cl[i].cost_center) cl[i].cost_center = d.cost_center; if(!cl[i].cost_center) cl[i].cost_center = d.cost_center;

View File

@ -55,11 +55,6 @@ class PurchaseInvoice(BuyingController):
if not self.on_hold: if not self.on_hold:
self.release_date = '' self.release_date = ''
def before_print(self):
self.gl_entries = frappe.get_list("GL Entry",filters={"voucher_type": "Purchase Invoice",
"voucher_no": self.name} ,
fields=["account", "party_type", "party", "debit", "credit"]
)
def invoice_is_blocked(self): def invoice_is_blocked(self):
return self.on_hold and (not self.release_date or self.release_date > getdate(nowdate())) return self.on_hold and (not self.release_date or self.release_date > getdate(nowdate()))
@ -770,10 +765,6 @@ class PurchaseInvoice(BuyingController):
self.update_status_updater_args() self.update_status_updater_args()
if not self.is_return: if not self.is_return:
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
if frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'):
unlink_ref_doc_from_payment_entries(self)
self.update_prevdoc_status() self.update_prevdoc_status()
self.update_billing_status_for_zero_amount_refdoc("Purchase Order") self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
self.update_billing_status_in_pr() self.update_billing_status_in_pr()

View File

@ -89,6 +89,12 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
}, __('Create')); }, __('Create'));
} }
if (doc.docstatus === 1) {
cur_frm.add_custom_button(__('Maintenance Schedule'), function () {
cur_frm.cscript.make_maintenance_schedule();
}, __('Create'));
}
if(!doc.auto_repeat) { if(!doc.auto_repeat) {
cur_frm.add_custom_button(__('Subscription'), function() { cur_frm.add_custom_button(__('Subscription'), function() {
erpnext.utils.make_subscription(doc.doctype, doc.name) erpnext.utils.make_subscription(doc.doctype, doc.name)
@ -118,6 +124,13 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
} }
}, },
make_maintenance_schedule: function() {
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_maintenance_schedule",
frm: cur_frm
})
},
on_submit: function(doc, dt, dn) { on_submit: function(doc, dt, dn) {
var me = this; var me = this;

View File

@ -205,18 +205,11 @@ class SalesInvoice(SellingController):
def before_cancel(self): def before_cancel(self):
self.update_time_sheet(None) self.update_time_sheet(None)
def before_print(self):
self.gl_entries = frappe.get_list("GL Entry",filters={"voucher_type": "Sales Invoice",
"voucher_no": self.name} ,
fields=["account", "party_type", "party", "debit", "credit"]
)
def on_cancel(self): def on_cancel(self):
self.check_sales_order_on_hold_or_close("sales_order") super(SalesInvoice, self).on_cancel()
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries self.check_sales_order_on_hold_or_close("sales_order")
if frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'):
unlink_ref_doc_from_payment_entries(self)
if self.is_return and not self.update_billed_amount_in_sales_order: if self.is_return and not self.update_billed_amount_in_sales_order:
# NOTE status updating bypassed for is_return # NOTE status updating bypassed for is_return
@ -1230,6 +1223,22 @@ def get_bank_cash_account(mode_of_payment, company):
"account": account "account": account
} }
@frappe.whitelist()
def make_maintenance_schedule(source_name, target_doc=None):
doclist = get_mapped_doc("Sales Invoice", source_name, {
"Sales Invoice": {
"doctype": "Maintenance Schedule",
"validation": {
"docstatus": ["=", 1]
}
},
"Sales Invoice Item": {
"doctype": "Maintenance Schedule Item",
},
}, target_doc)
return doclist
@frappe.whitelist() @frappe.whitelist()
def make_delivery_note(source_name, target_doc=None): def make_delivery_note(source_name, target_doc=None):
def set_missing_values(source, target): def set_missing_values(source, target):
@ -1388,4 +1397,4 @@ def get_loyalty_programs(customer):
frappe.db.set(customer, 'loyalty_program', lp_details[0]) frappe.db.set(customer, 'loyalty_program', lp_details[0])
return [] return []
else: else:
return lp_details return lp_details

View File

@ -14,8 +14,9 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_per
from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency
from erpnext.stock.doctype.serial_no.serial_no import SerialNoWarehouseError from erpnext.stock.doctype.serial_no.serial_no import SerialNoWarehouseError
from frappe.model.naming import make_autoname from frappe.model.naming import make_autoname
from erpnext.accounts.doctype.account.test_account import get_inventory_account from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account
from erpnext.controllers.taxes_and_totals import get_itemised_tax_breakup_data from erpnext.controllers.taxes_and_totals import get_itemised_tax_breakup_data
from erpnext.stock.doctype.item.test_item import create_item
from six import iteritems from six import iteritems
class TestSalesInvoice(unittest.TestCase): class TestSalesInvoice(unittest.TestCase):
def make(self): def make(self):
@ -1572,6 +1573,56 @@ class TestSalesInvoice(unittest.TestCase):
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
accounts_settings.save() accounts_settings.save()
def test_deferred_revenue(self):
deferred_account = create_account(account_name="Deferred Revenue",
parent_account="Current Liabilities - _TC", company="_Test Company")
item = create_item("_Test Item for Deferred Accounting")
item.enable_deferred_revenue = 1
item.deferred_revenue_account = deferred_account
item.no_of_months = 12
item.save()
si = create_sales_invoice(item=item.name, posting_date="2019-01-10", do_not_submit=True)
si.items[0].enable_deferred_revenue = 1
si.items[0].service_start_date = "2019-01-10"
si.items[0].service_end_date = "2019-03-15"
si.items[0].deferred_revenue_account = deferred_account
si.save()
si.submit()
from erpnext.accounts.deferred_revenue import convert_deferred_revenue_to_income
convert_deferred_revenue_to_income(start_date="2019-01-01", end_date="2019-01-31")
expected_gle = [
[deferred_account, 33.85, 0.0, "2019-01-31"],
["Sales - _TC", 0.0, 33.85, "2019-01-31"]
]
self.check_gl_entries(si.name, expected_gle, "2019-01-10")
convert_deferred_revenue_to_income(start_date="2019-01-01", end_date="2019-03-31")
expected_gle = [
[deferred_account, 43.08, 0.0, "2019-02-28"],
["Sales - _TC", 0.0, 43.08, "2019-02-28"],
[deferred_account, 23.07, 0.0, "2019-03-15"],
["Sales - _TC", 0.0, 23.07, "2019-03-15"]
]
self.check_gl_entries(si.name, expected_gle, "2019-01-31")
def check_gl_entries(self, voucher_no, expected_gle, posting_date):
gl_entries = frappe.db.sql("""select account, debit, credit, posting_date
from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s and posting_date > %s
order by posting_date asc, account asc""", (voucher_no, posting_date), as_dict=1)
for i, gle in enumerate(gl_entries):
self.assertEqual(expected_gle[i][0], gle.account)
self.assertEqual(expected_gle[i][1], gle.debit)
self.assertEqual(expected_gle[i][2], gle.credit)
self.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)
def create_sales_invoice(**args): def create_sales_invoice(**args):
si = frappe.new_doc("Sales Invoice") si = frappe.new_doc("Sales Invoice")
@ -1669,4 +1720,4 @@ def get_outstanding_amount(against_voucher_type, against_voucher, account, party
if against_voucher_type == 'Purchase Invoice': if against_voucher_type == 'Purchase Invoice':
bal = bal * -1 bal = bal * -1
return bal return bal

View File

@ -333,6 +333,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
var me = this; var me = this;
this.frm = {} this.frm = {}
this.load_data(true); this.load_data(true);
this.frm.doc.offline_pos_name = '';
this.setup(); this.setup();
this.set_default_customer() this.set_default_customer()
}, },
@ -345,7 +346,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
if (load_doc) { if (load_doc) {
this.frm.doc = JSON.parse(localStorage.getItem('doc')); this.frm.doc = JSON.parse(localStorage.getItem('doc'));
this.frm.doc.offline_pos_name = null;
} }
$.each(this.meta, function (i, data) { $.each(this.meta, function (i, data) {
@ -641,7 +641,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
me.list_customers_btn.toggleClass("view_customer"); me.list_customers_btn.toggleClass("view_customer");
me.pos_bill.show(); me.pos_bill.show();
me.list_customers_btn.show(); me.list_customers_btn.show();
me.frm.doc.offline_pos_name = $(this).parents().attr('invoice-name') me.frm.doc.offline_pos_name = $(this).parents().attr('invoice-name');
me.edit_record(); me.edit_record();
}) })
@ -984,7 +984,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
} }
if(!this.customer_doc.fields_dict.customer_pos_id.value) { if(!this.customer_doc.fields_dict.customer_pos_id.value) {
this.customer_doc.set_value("customer_pos_id", $.now()) this.customer_doc.set_value("customer_pos_id", frappe.datetime.now_datetime())
} }
}, },
@ -1686,10 +1686,18 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
create_invoice: function () { create_invoice: function () {
var me = this; var me = this;
var existing_pos_list = [];
var invoice_data = {}; var invoice_data = {};
this.si_docs = this.get_doc_from_localstorage(); this.si_docs = this.get_doc_from_localstorage();
if (this.frm.doc.offline_pos_name) { if(this.si_docs) {
this.si_docs.forEach((row) => {
existing_pos_list.push(Object.keys(row));
});
}
if (this.frm.doc.offline_pos_name
&& in_list(existing_pos_list, this.frm.doc.offline_pos_name)) {
this.update_invoice() this.update_invoice()
//to retrieve and set the default payment //to retrieve and set the default payment
invoice_data[this.frm.doc.offline_pos_name] = this.frm.doc; invoice_data[this.frm.doc.offline_pos_name] = this.frm.doc;
@ -1698,8 +1706,8 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.frm.doc.paid_amount = this.frm.doc.net_total this.frm.doc.paid_amount = this.frm.doc.net_total
this.frm.doc.outstanding_amount = 0 this.frm.doc.outstanding_amount = 0
} else { } else if(!this.frm.doc.offline_pos_name) {
this.frm.doc.offline_pos_name = $.now(); this.frm.doc.offline_pos_name = frappe.datetime.now_datetime();
this.frm.doc.posting_date = frappe.datetime.get_today(); this.frm.doc.posting_date = frappe.datetime.get_today();
this.frm.doc.posting_time = frappe.datetime.now_time(); this.frm.doc.posting_time = frappe.datetime.now_time();
this.frm.doc.pos_total_qty = this.frm.doc.qty_total; this.frm.doc.pos_total_qty = this.frm.doc.qty_total;

View File

@ -592,13 +592,17 @@ def get_party_shipping_address(doctype, name):
else: else:
return '' return ''
def get_partywise_advanced_payment_amount(party_type="Customer"): def get_partywise_advanced_payment_amount(party_type, posting_date = None):
cond = "1=1"
if posting_date:
cond = "posting_date <= '{0}'".format(posting_date)
data = frappe.db.sql(""" SELECT party, sum({0}) as amount data = frappe.db.sql(""" SELECT party, sum({0}) as amount
FROM `tabGL Entry` FROM `tabGL Entry`
WHERE WHERE
party_type = %s and against_voucher is null party_type = %s and against_voucher is null
GROUP BY party""" and {1} GROUP BY party"""
.format(("credit") if party_type == "Customer" else "debit") , party_type) .format(("credit") if party_type == "Customer" else "debit", cond) , party_type)
if data: if data:
return frappe._dict(data) return frappe._dict(data)

View File

@ -136,7 +136,8 @@ class AccountsReceivableSummary(ReceivablePayableReport):
partywise_total = self.get_partywise_total(party_naming_by, args) partywise_total = self.get_partywise_total(party_naming_by, args)
partywise_advance_amount = get_partywise_advanced_payment_amount(args.get("party_type")) or {} partywise_advance_amount = get_partywise_advanced_payment_amount(args.get("party_type"),
self.filters.get("report_date")) or {}
for party, party_dict in iteritems(partywise_total): for party, party_dict in iteritems(partywise_total):
row = [party] row = [party]
@ -144,7 +145,10 @@ class AccountsReceivableSummary(ReceivablePayableReport):
row += [self.get_party_name(args.get("party_type"), party)] row += [self.get_party_name(args.get("party_type"), party)]
row += [partywise_advance_amount.get(party, 0)] row += [partywise_advance_amount.get(party, 0)]
paid_amt = flt(party_dict.paid_amt - partywise_advance_amount.get(party, 0))
paid_amt = 0
if party_dict.paid_amt > 0:
paid_amt = flt(party_dict.paid_amt - partywise_advance_amount.get(party, 0))
row += [ row += [
party_dict.invoiced_amt, paid_amt, party_dict.credit_amt, party_dict.outstanding_amt, party_dict.invoiced_amt, paid_amt, party_dict.credit_amt, party_dict.outstanding_amt,

View File

@ -14,13 +14,13 @@ def execute(filters=None):
def get_column(): def get_column():
return [ return [
_("Delivery Note") + ":Link/Delivery Note:120", _("Date") + ":Date:100", _("Delivery Note") + ":Link/Delivery Note:120", _("Status") + "::120", _("Date") + ":Date:100",
_("Suplier") + ":Link/Customer:120", _("Customer Name") + "::120", _("Suplier") + ":Link/Customer:120", _("Customer Name") + "::120",
_("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120", _("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120",
_("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Pending Amount") + ":Currency:100", _("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Pending Amount") + ":Currency:100",
_("Item Name") + "::120", _("Description") + "::120", _("Company") + ":Link/Company:120", _("Item Name") + "::120", _("Description") + "::120", _("Company") + ":Link/Company:120",
] ]
def get_args(): def get_args():
return {'doctype': 'Delivery Note', 'party': 'customer', return {'doctype': 'Delivery Note', 'party': 'customer',
'date': 'posting_date', 'order': 'name', 'order_by': 'desc'} 'date': 'posting_date', 'order': 'name', 'order_by': 'desc'}

View File

@ -12,14 +12,14 @@ def get_ordered_to_be_billed_data(args):
child_tab = doctype + " Item" child_tab = doctype + " Item"
precision = get_field_precision(frappe.get_meta(child_tab).get_field("billed_amt"), precision = get_field_precision(frappe.get_meta(child_tab).get_field("billed_amt"),
currency=get_default_currency()) or 2 currency=get_default_currency()) or 2
project_field = get_project_field(doctype, party) project_field = get_project_field(doctype, party)
return frappe.db.sql(""" return frappe.db.sql("""
Select Select
`{parent_tab}`.name, `{parent_tab}`.{date_field}, `{parent_tab}`.{party}, `{parent_tab}`.{party}_name, `{parent_tab}`.name, `{parent_tab}`.status, `{parent_tab}`.{date_field}, `{parent_tab}`.{party}, `{parent_tab}`.{party}_name,
{project_field}, `{child_tab}`.item_code, `{child_tab}`.base_amount, {project_field}, `{child_tab}`.item_code, `{child_tab}`.base_amount,
(`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1)), (`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1)),
(`{child_tab}`.base_amount - (`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1))), (`{child_tab}`.base_amount - (`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1))),
`{child_tab}`.item_name, `{child_tab}`.description, `{parent_tab}`.company `{child_tab}`.item_name, `{child_tab}`.description, `{parent_tab}`.company
from from

View File

@ -14,13 +14,13 @@ def execute(filters=None):
def get_column(): def get_column():
return [ return [
_("Sales Order") + ":Link/Sales Order:120", _("Date") + ":Date:100", _("Sales Order") + ":Link/Sales Order:120", _("Status") + "::120", _("Date") + ":Date:100",
_("Suplier") + ":Link/Customer:120", _("Customer Name") + "::120", _("Suplier") + ":Link/Customer:120", _("Customer Name") + "::120",
_("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120", _("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120",
_("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Pending Amount") + ":Currency:100", _("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Pending Amount") + ":Currency:100",
_("Item Name") + "::120", _("Description") + "::120", _("Company") + ":Link/Company:120", _("Item Name") + "::120", _("Description") + "::120", _("Company") + ":Link/Company:120",
] ]
def get_args(): def get_args():
return {'doctype': 'Sales Order', 'party': 'customer', return {'doctype': 'Sales Order', 'party': 'customer',
'date': 'transaction_date', 'order': 'transaction_date', 'order_by': 'asc'} 'date': 'transaction_date', 'order': 'transaction_date', 'order_by': 'asc'}

View File

@ -133,6 +133,13 @@ def get_columns(filters):
"options": filters.get("based_on"), "options": filters.get("based_on"),
"width": 300 "width": 300
}, },
{
"fieldname": "currency",
"label": _("Currency"),
"fieldtype": "Link",
"options": "Currency",
"hidden": 1
},
{ {
"fieldname": "income", "fieldname": "income",
"label": _("Income"), "label": _("Income"),
@ -153,13 +160,6 @@ def get_columns(filters):
"fieldtype": "Currency", "fieldtype": "Currency",
"options": "currency", "options": "currency",
"width": 120 "width": 120
},
{
"fieldname": "currency",
"label": _("Currency"),
"fieldtype": "Link",
"options": "Currency",
"hidden": 1
} }
] ]
@ -191,4 +191,4 @@ def set_gl_entries_by_account(company, from_date, to_date, based_on, gl_entries_
for entry in gl_entries: for entry in gl_entries:
gl_entries_by_account.setdefault(entry.based_on, []).append(entry) gl_entries_by_account.setdefault(entry.based_on, []).append(entry)
return gl_entries_by_account return gl_entries_by_account

View File

@ -14,13 +14,13 @@ def execute(filters=None):
def get_column(): def get_column():
return [ return [
_("Purchase Order") + ":Link/Purchase Order:120", _("Date") + ":Date:100", _("Purchase Order") + ":Link/Purchase Order:120", _("Status") + "::120", _("Date") + ":Date:100",
_("Suplier") + ":Link/Supplier:120", _("Suplier Name") + "::120", _("Suplier") + ":Link/Supplier:120", _("Suplier Name") + "::120",
_("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120", _("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120",
_("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Amount to Bill") + ":Currency:100", _("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Amount to Bill") + ":Currency:100",
_("Item Name") + "::120", _("Description") + "::120", _("Company") + ":Link/Company:120", _("Item Name") + "::120", _("Description") + "::120", _("Company") + ":Link/Company:120",
] ]
def get_args(): def get_args():
return {'doctype': 'Purchase Order', 'party': 'supplier', return {'doctype': 'Purchase Order', 'party': 'supplier',
'date': 'transaction_date', 'order': 'transaction_date', 'order_by': 'asc'} 'date': 'transaction_date', 'order': 'transaction_date', 'order_by': 'asc'}

View File

@ -14,7 +14,7 @@ def execute(filters=None):
def get_column(): def get_column():
return [ return [
_("Purchase Receipt") + ":Link/Purchase Receipt:120", _("Date") + ":Date:100", _("Purchase Receipt") + ":Link/Purchase Receipt:120", _("Status") + "::120", _("Date") + ":Date:100",
_("Supplier") + ":Link/Supplier:120", _("Supplier Name") + "::120", _("Supplier") + ":Link/Supplier:120", _("Supplier Name") + "::120",
_("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120", _("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120",
_("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Amount to Bill") + ":Currency:100", _("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Amount to Bill") + ":Currency:100",

View File

@ -25,9 +25,6 @@ frappe.ui.form.on("Purchase Order", {
frm.set_indicator_formatter('item_code', frm.set_indicator_formatter('item_code',
function(doc) { return (doc.qty<=doc.received_qty) ? "green" : "orange" }) function(doc) { return (doc.qty<=doc.received_qty) ? "green" : "orange" })
frm.set_indicator_formatter('pricing_rule',
function(doc) { return (doc.rule_applied) ? "green" : "red" })
frm.set_query("blanket_order", "items", function() { frm.set_query("blanket_order", "items", function() {
return { return {
filters: { filters: {

View File

@ -444,7 +444,7 @@ def make_rm_stock_entry(purchase_order, rm_items):
item_wh = get_item_details(items) item_wh = get_item_details(items)
stock_entry = frappe.new_doc("Stock Entry") stock_entry = frappe.new_doc("Stock Entry")
stock_entry.purpose = "Subcontract" stock_entry.purpose = "Send to Subcontractor"
stock_entry.purchase_order = purchase_order.name stock_entry.purchase_order = purchase_order.name
stock_entry.supplier = purchase_order.supplier stock_entry.supplier = purchase_order.supplier
stock_entry.supplier_name = purchase_order.supplier_name stock_entry.supplier_name = purchase_order.supplier_name
@ -452,6 +452,7 @@ def make_rm_stock_entry(purchase_order, rm_items):
stock_entry.address_display = purchase_order.address_display stock_entry.address_display = purchase_order.address_display
stock_entry.company = purchase_order.company stock_entry.company = purchase_order.company
stock_entry.to_warehouse = purchase_order.supplier_warehouse stock_entry.to_warehouse = purchase_order.supplier_warehouse
stock_entry.set_stock_entry_type()
for item_code in fg_items: for item_code in fg_items:
for rm_item_data in rm_items_list: for rm_item_data in rm_items_list:

View File

@ -465,6 +465,33 @@ class TestPurchaseOrder(unittest.TestCase):
self.assertEquals(se_items, supplied_items) self.assertEquals(se_items, supplied_items)
update_backflush_based_on("BOM") update_backflush_based_on("BOM")
def test_advance_payment_entry_unlink_against_purchase_order(self):
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
frappe.db.set_value("Accounts Settings", "Accounts Settings",
"unlink_advance_payment_on_cancelation_of_order", 1)
po_doc = create_purchase_order()
pe = get_payment_entry("Purchase Order", po_doc.name, bank_account="_Test Bank - _TC")
pe.reference_no = "1"
pe.reference_date = nowdate()
pe.paid_from_account_currency = po_doc.currency
pe.paid_to_account_currency = po_doc.currency
pe.source_exchange_rate = 1
pe.target_exchange_rate = 1
pe.paid_amount = po_doc.grand_total
pe.save(ignore_permissions=True)
pe.submit()
po_doc = frappe.get_doc('Purchase Order', po_doc.name)
po_doc.cancel()
pe_doc = frappe.get_doc('Payment Entry', pe.name)
pe_doc.cancel()
frappe.db.set_value("Accounts Settings", "Accounts Settings",
"unlink_advance_payment_on_cancelation_of_order", 0)
def make_subcontracted_item(item_code): def make_subcontracted_item(item_code):
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom

View File

@ -81,9 +81,9 @@ def get_data():
"description": "Sales pipeline, leads, opportunities and customers." "description": "Sales pipeline, leads, opportunities and customers."
}, },
{ {
"module_name": "Help Desk", "module_name": "Support",
"category": "Modules", "category": "Modules",
"label": _("Help Desk"), "label": _("Support"),
"color": "#1abc9c", "color": "#1abc9c",
"icon": "fa fa-check-square-o", "icon": "fa fa-check-square-o",
"type": "module", "type": "module",

View File

@ -1,82 +0,0 @@
from __future__ import unicode_literals
from frappe import _
def get_data():
return [
{
"label": _("Issues"),
"items": [
{
"type": "doctype",
"name": "Issue",
"description": _("Support queries from customers."),
"onboard": 1,
},
{
"type": "doctype",
"name": "Communication",
"description": _("Communication log."),
"onboard": 1,
},
]
},
{
"label": _("Warranty"),
"items": [
{
"type": "doctype",
"name": "Warranty Claim",
"description": _("Warranty Claim against Serial No."),
},
{
"type": "doctype",
"name": "Serial No",
"description": _("Single unit of an Item."),
},
]
},
{
"label": _("Service Level Agreement"),
"items": [
{
"type": "doctype",
"name": "Employee Group",
"description": _("Support Team."),
},
{
"type": "doctype",
"name": "Service Level",
"description": _("Service Level."),
},
{
"type": "doctype",
"name": "Service Level Agreement",
"description": _("Service Level Agreement."),
}
]
},
{
"label": _("Reports"),
"icon": "fa fa-list",
"items": [
{
"type": "page",
"name": "support-analytics",
"label": _("Support Analytics"),
"icon": "fa fa-bar-chart"
},
{
"type": "report",
"name": "Minutes to First Response for Issues",
"doctype": "Issue",
"is_query_report": True
},
{
"type": "report",
"name": "Support Hours",
"doctype": "Issue",
"is_query_report": True
},
]
},
]

View File

@ -287,6 +287,11 @@ def get_data():
"name": "Employee Advance", "name": "Employee Advance",
"dependencies": ["Employee"] "dependencies": ["Employee"]
}, },
{
"type": "doctype",
"name": "Expense Claim",
"dependencies": ["Employee"]
},
{ {
"type": "doctype", "type": "doctype",
"name": "Loan Type", "name": "Loan Type",
@ -296,6 +301,10 @@ def get_data():
"name": "Loan Application", "name": "Loan Application",
"dependencies": ["Employee"] "dependencies": ["Employee"]
}, },
{
"type": "doctype",
"name": "Loan"
}
] ]
}, },
{ {

View File

@ -27,6 +27,13 @@ def get_data():
"onboard": 1, "onboard": 1,
"dependencies": ["Item", "Customer"], "dependencies": ["Item", "Customer"],
}, },
{
"type": "doctype",
"name": "Sales Invoice",
"description": _("Invoices for Costumers."),
"onboard": 1,
"dependencies": ["Item", "Customer"],
},
{ {
"type": "doctype", "type": "doctype",
"name": "Sales Partner", "name": "Sales Partner",

View File

@ -10,11 +10,13 @@ def get_data():
"type": "doctype", "type": "doctype",
"name": "Issue", "name": "Issue",
"description": _("Support queries from customers."), "description": _("Support queries from customers."),
"onboard": 1,
}, },
{ {
"type": "doctype", "type": "doctype",
"name": "Communication", "name": "Communication",
"description": _("Communication log."), "description": _("Communication log."),
"onboard": 1,
}, },
] ]
}, },
@ -33,6 +35,26 @@ def get_data():
}, },
] ]
}, },
{
"label": _("Service Level Agreement"),
"items": [
{
"type": "doctype",
"name": "Employee Group",
"description": _("Support Team."),
},
{
"type": "doctype",
"name": "Service Level",
"description": _("Service Level."),
},
{
"type": "doctype",
"name": "Service Level Agreement",
"description": _("Service Level Agreement."),
}
]
},
{ {
"label": _("Reports"), "label": _("Reports"),
"icon": "fa fa-list", "icon": "fa fa-list",
@ -57,24 +79,4 @@ def get_data():
}, },
] ]
}, },
{ ]
"label": _("Service Level Agreement"),
"items": [
{
"type": "doctype",
"name": "Employee Group",
"description": _("Support Team."),
},
{
"type": "doctype",
"name": "Service Level",
"description": _("Service Level."),
},
{
"type": "doctype",
"name": "Service Level Agreement",
"description": _("Service Level Agreement."),
}
]
},
]

View File

@ -119,6 +119,12 @@ class AccountsController(TransactionBase):
self.validate_non_invoice_documents_schedule() self.validate_non_invoice_documents_schedule()
def before_print(self): def before_print(self):
if self.doctype in ['Journal Entry', 'Payment Entry', 'Sales Invoice', 'Purchase Invoice']:
self.gl_entries = frappe.get_list("GL Entry", filters={
"voucher_type": self.doctype,
"voucher_no": self.name
}, fields=["account", "party_type", "party", "debit", "credit", "remarks"])
if self.doctype in ['Purchase Order', 'Sales Order', 'Sales Invoice', 'Purchase Invoice', if self.doctype in ['Purchase Order', 'Sales Order', 'Sales Invoice', 'Purchase Invoice',
'Supplier Quotation', 'Purchase Receipt', 'Delivery Note', 'Quotation']: 'Supplier Quotation', 'Purchase Receipt', 'Delivery Note', 'Quotation']:
if self.get("group_same_items"): if self.get("group_same_items"):
@ -276,7 +282,7 @@ class AccountsController(TransactionBase):
if self.doctype in ["Purchase Invoice", "Sales Invoice"] and item.meta.get_field('is_fixed_asset'): if self.doctype in ["Purchase Invoice", "Sales Invoice"] and item.meta.get_field('is_fixed_asset'):
item.set('is_fixed_asset', ret.get('is_fixed_asset', 0)) item.set('is_fixed_asset', ret.get('is_fixed_asset', 0))
if ret.get("pricing_rules"): if ret.get("pricing_rules") and not ret.get("validate_applied_rule", 0):
# if user changed the discount percentage then set user's discount percentage ? # if user changed the discount percentage then set user's discount percentage ?
item.set("pricing_rules", ret.get("pricing_rules")) item.set("pricing_rules", ret.get("pricing_rules"))
item.set("discount_percentage", ret.get("discount_percentage")) item.set("discount_percentage", ret.get("discount_percentage"))
@ -546,6 +552,19 @@ class AccountsController(TransactionBase):
from erpnext.accounts.utils import reconcile_against_document from erpnext.accounts.utils import reconcile_against_document
reconcile_against_document(lst) reconcile_against_document(lst)
def on_cancel(self):
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
if self.doctype in ["Sales Invoice", "Purchase Invoice"]:
if self.is_return: return
if frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'):
unlink_ref_doc_from_payment_entries(self)
elif self.doctype in ["Sales Order", "Purchase Order"]:
if frappe.db.get_single_value('Accounts Settings', 'unlink_advance_payment_on_cancelation_of_order'):
unlink_ref_doc_from_payment_entries(self)
def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield): def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield):
from erpnext.controllers.status_updater import get_tolerance_for from erpnext.controllers.status_updater import get_tolerance_for
item_tolerance = {} item_tolerance = {}

View File

@ -538,6 +538,8 @@ class BuyingController(StockController):
update_last_purchase_rate(self, is_submit = 1) update_last_purchase_rate(self, is_submit = 1)
def on_cancel(self): def on_cancel(self):
super(BuyingController, self).on_cancel()
if self.get('is_return'): if self.get('is_return'):
return return
@ -736,7 +738,7 @@ def get_subcontracted_raw_materials_from_se(purchase_orders):
sed.stock_uom, sed.subcontracted_item as main_item_code, sed.serial_no, sed.batch_no sed.stock_uom, sed.subcontracted_item as main_item_code, sed.serial_no, sed.batch_no
from `tabStock Entry` se,`tabStock Entry Detail` sed from `tabStock Entry` se,`tabStock Entry Detail` sed
where where
se.name = sed.parent and se.docstatus=1 and se.purpose='Subcontract' se.name = sed.parent and se.docstatus=1 and se.purpose='Send to Subcontractor'
and se.purchase_order in (%s) and ifnull(sed.t_warehouse, '') != '' and se.purchase_order in (%s) and ifnull(sed.t_warehouse, '') != ''
group by sed.item_code, sed.t_warehouse group by sed.item_code, sed.t_warehouse
""" % (','.join(['%s'] * len(purchase_orders))), tuple(purchase_orders), as_dict=1) """ % (','.join(['%s'] * len(purchase_orders))), tuple(purchase_orders), as_dict=1)

View File

@ -152,7 +152,6 @@ standard_portal_menu_items = [
{"title": _("Issues"), "route": "/issues", "reference_doctype": "Issue", "role":"Customer"}, {"title": _("Issues"), "route": "/issues", "reference_doctype": "Issue", "role":"Customer"},
{"title": _("Addresses"), "route": "/addresses", "reference_doctype": "Address"}, {"title": _("Addresses"), "route": "/addresses", "reference_doctype": "Address"},
{"title": _("Timesheets"), "route": "/timesheets", "reference_doctype": "Timesheet", "role":"Customer"}, {"title": _("Timesheets"), "route": "/timesheets", "reference_doctype": "Timesheet", "role":"Customer"},
{"title": _("Timesheets"), "route": "/timesheets", "reference_doctype": "Timesheet", "role":"Customer"},
{"title": _("Lab Test"), "route": "/lab-test", "reference_doctype": "Lab Test", "role":"Patient"}, {"title": _("Lab Test"), "route": "/lab-test", "reference_doctype": "Lab Test", "role":"Patient"},
{"title": _("Prescription"), "route": "/prescription", "reference_doctype": "Patient Encounter", "role":"Patient"}, {"title": _("Prescription"), "route": "/prescription", "reference_doctype": "Patient Encounter", "role":"Patient"},
{"title": _("Patient Appointment"), "route": "/patient-appointments", "reference_doctype": "Patient Appointment", "role":"Patient"}, {"title": _("Patient Appointment"), "route": "/patient-appointments", "reference_doctype": "Patient Appointment", "role":"Patient"},
@ -241,8 +240,7 @@ scheduler_events = {
"erpnext.erpnext_integrations.doctype.amazon_mws_settings.amazon_mws_settings.schedule_get_order_details", "erpnext.erpnext_integrations.doctype.amazon_mws_settings.amazon_mws_settings.schedule_get_order_details",
"erpnext.accounts.doctype.gl_entry.gl_entry.rename_gle_sle_docs", "erpnext.accounts.doctype.gl_entry.gl_entry.rename_gle_sle_docs",
"erpnext.projects.doctype.project.project.hourly_reminder", "erpnext.projects.doctype.project.project.hourly_reminder",
"erpnext.projects.doctype.project.project.collect_project_status", "erpnext.projects.doctype.project.project.collect_project_status"
"erpnext.support.doctype.issue.issue.update_support_timer",
], ],
"daily": [ "daily": [
"erpnext.stock.reorder_item.reorder_item", "erpnext.stock.reorder_item.reorder_item",
@ -333,4 +331,4 @@ user_privacy_documents = [
'match_field': 'contact_email', 'match_field': 'contact_email',
'personal_fields': ['contact_mobile', 'contact_display', 'customer_name'], 'personal_fields': ['contact_mobile', 'contact_display', 'customer_name'],
} }
] ]

View File

@ -400,6 +400,19 @@ def get_leave_balance_on(employee, leave_type, date, allocation_records=None, do
return flt(allocation.total_leaves_allocated) - (flt(leaves_taken) + flt(leaves_encashed)) return flt(allocation.total_leaves_allocated) - (flt(leaves_taken) + flt(leaves_encashed))
def get_total_allocated_leaves(employee, leave_type, date):
filters= {
'from_date': ['<=', date],
'to_date': ['>=', date],
'docstatus': 1,
'leave_type': leave_type,
'employee': employee
}
leave_allocation_records = frappe.db.get_all('Leave Allocation', filters=filters, fields=['total_leaves_allocated'])
return flt(leave_allocation_records[0]['total_leaves_allocated']) if leave_allocation_records else flt(0)
def get_leaves_for_period(employee, leave_type, from_date, to_date, status, docname=None): def get_leaves_for_period(employee, leave_type, from_date, to_date, status, docname=None):
leave_applications = frappe.db.sql(""" leave_applications = frappe.db.sql("""
select name, employee, leave_type, from_date, to_date, total_leave_days select name, employee, leave_type, from_date, to_date, total_leave_days

View File

@ -441,7 +441,7 @@ class SalarySlip(TransactionBase):
def calculate_net_pay(self): def calculate_net_pay(self):
if self.salary_structure: if self.salary_structure:
self.calculate_component_amounts() self.calculate_component_amounts()
disable_rounded_total = cint(frappe.db.get_value("Global Defaults", None, "disable_rounded_total")) disable_rounded_total = cint(frappe.db.get_value("Global Defaults", None, "disable_rounded_total"))
precision = frappe.defaults.get_global_default("currency_precision") precision = frappe.defaults.get_global_default("currency_precision")
self.total_deduction = 0 self.total_deduction = 0
@ -452,10 +452,13 @@ class SalarySlip(TransactionBase):
self.set_loan_repayment() self.set_loan_repayment()
self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment)) 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.rounded_total = rounded(self.net_pay, self.rounded_total = rounded(self.net_pay,
self.precision("net_pay") if disable_rounded_total else 0) self.precision("net_pay") if disable_rounded_total else 0)
if self.net_pay < 0: if self.net_pay < 0:
frappe.throw(_("Net Pay cannnot be negative")) frappe.throw(_("Net Pay cannnot be negative"))

View File

@ -5,21 +5,21 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from erpnext.hr.doctype.leave_application.leave_application \ from erpnext.hr.doctype.leave_application.leave_application \
import get_leave_allocation_records, get_leave_balance_on, get_approved_leaves_for_period import get_leave_allocation_records, get_leave_balance_on, get_approved_leaves_for_period, get_total_allocated_leaves
def execute(filters=None): def execute(filters=None):
leave_types = frappe.db.sql_list("select name from `tabLeave Type` order by name asc") leave_types = frappe.db.sql_list("select name from `tabLeave Type` order by name asc")
columns = get_columns(leave_types) columns = get_columns(leave_types)
data = get_data(filters, leave_types) data = get_data(filters, leave_types)
return columns, data return columns, data
def get_columns(leave_types): def get_columns(leave_types):
columns = [ columns = [
_("Employee") + ":Link/Employee:150", _("Employee") + ":Link/Employee:150",
_("Employee Name") + "::200", _("Employee Name") + "::200",
_("Department") +"::150" _("Department") +"::150"
] ]
@ -27,18 +27,18 @@ def get_columns(leave_types):
columns.append(_(leave_type) + " " + _("Opening") + ":Float:160") columns.append(_(leave_type) + " " + _("Opening") + ":Float:160")
columns.append(_(leave_type) + " " + _("Taken") + ":Float:160") columns.append(_(leave_type) + " " + _("Taken") + ":Float:160")
columns.append(_(leave_type) + " " + _("Balance") + ":Float:160") columns.append(_(leave_type) + " " + _("Balance") + ":Float:160")
return columns return columns
def get_data(filters, leave_types): def get_data(filters, leave_types):
user = frappe.session.user user = frappe.session.user
allocation_records_based_on_to_date = get_leave_allocation_records(filters.to_date) allocation_records_based_on_to_date = get_leave_allocation_records(filters.to_date)
allocation_records_based_on_from_date = get_leave_allocation_records(filters.from_date) allocation_records_based_on_from_date = get_leave_allocation_records(filters.from_date)
active_employees = frappe.get_all("Employee", active_employees = frappe.get_all("Employee",
filters = { "status": "Active", "company": filters.company}, filters = { "status": "Active", "company": filters.company},
fields = ["name", "employee_name", "department", "user_id"]) fields = ["name", "employee_name", "department", "user_id"])
data = [] data = []
for employee in active_employees: for employee in active_employees:
leave_approvers = get_approvers(employee.department) leave_approvers = get_approvers(employee.department)
@ -51,8 +51,7 @@ def get_data(filters, leave_types):
filters.from_date, filters.to_date) filters.from_date, filters.to_date)
# opening balance # opening balance
opening = get_leave_balance_on(employee.name, leave_type, filters.from_date, opening = get_total_allocated_leaves(employee.name, leave_type, filters.to_date)
allocation_records_based_on_from_date.get(employee.name, frappe._dict()))
# closing balance # closing balance
closing = get_leave_balance_on(employee.name, leave_type, filters.to_date, closing = get_leave_balance_on(employee.name, leave_type, filters.to_date,
@ -61,7 +60,7 @@ def get_data(filters, leave_types):
row += [opening, leaves_taken, closing] row += [opening, leaves_taken, closing]
data.append(row) data.append(row)
return data return data
def get_approvers(department): def get_approvers(department):

View File

@ -14,11 +14,11 @@ def execute(filters=None):
columns, earning_types, ded_types = get_columns(salary_slips) columns, earning_types, ded_types = get_columns(salary_slips)
ss_earning_map = get_ss_earning_map(salary_slips) ss_earning_map = get_ss_earning_map(salary_slips)
ss_ded_map = get_ss_ded_map(salary_slips) ss_ded_map = get_ss_ded_map(salary_slips)
doj_map = get_employee_doj_map()
data = [] data = []
for ss in salary_slips: for ss in salary_slips:
row = [ss.name, ss.employee, ss.employee_name, ss.branch, ss.department, ss.designation, row = [ss.name, ss.employee, ss.employee_name, doj_map.get(ss.employee), ss.branch, ss.department, ss.designation,
ss.company, ss.start_date, ss.end_date, ss.leave_without_pay, ss.payment_days] ss.company, ss.start_date, ss.end_date, ss.leave_without_pay, ss.payment_days]
if not ss.branch == None:columns[3] = columns[3].replace('-1','120') if not ss.branch == None:columns[3] = columns[3].replace('-1','120')
@ -44,17 +44,17 @@ def execute(filters=None):
def get_columns(salary_slips): def get_columns(salary_slips):
""" """
columns = [ columns = [
_("Salary Slip ID") + ":Link/Salary Slip:150",_("Employee") + ":Link/Employee:120", _("Employee Name") + "::140", _("Branch") + ":Link/Branch:120", _("Salary Slip ID") + ":Link/Salary Slip:150",_("Employee") + ":Link/Employee:120", _("Employee Name") + "::140",
_("Department") + ":Link/Department:120", _("Designation") + ":Link/Designation:120", _("Date of Joining") + "::80", _("Branch") + ":Link/Branch:120", _("Department") + ":Link/Department:120",
_("Company") + ":Link/Company:120", _("Start Date") + "::80", _("End Date") + "::80", _("Leave Without Pay") + ":Float:130", _("Designation") + ":Link/Designation:120", _("Company") + ":Link/Company:120", _("Start Date") + "::80",
_("Payment Days") + ":Float:120" _("End Date") + "::80", _("Leave Without Pay") + ":Float:130", _("Payment Days") + ":Float:120"
] ]
""" """
columns = [ columns = [
_("Salary Slip ID") + ":Link/Salary Slip:150",_("Employee") + ":Link/Employee:120", _("Employee Name") + "::140", _("Branch") + ":Link/Branch:-1", _("Salary Slip ID") + ":Link/Salary Slip:150",_("Employee") + ":Link/Employee:120", _("Employee Name") + "::140",
_("Department") + ":Link/Department:-1", _("Designation") + ":Link/Designation:-1", _("Date of Joining") + "::80", _("Branch") + ":Link/Branch:-1", _("Department") + ":Link/Department:-1",
_("Company") + ":Link/Company:120", _("Start Date") + "::80", _("End Date") + "::80", _("Leave Without Pay") + ":Float:-1", _("Designation") + ":Link/Designation:-1", _("Company") + ":Link/Company:120", _("Start Date") + "::80",
_("Payment Days") + ":Float:120" _("End Date") + "::80", _("Leave Without Pay") + ":Float:-1", _("Payment Days") + ":Float:120"
] ]
salary_components = {_("Earning"): [], _("Deduction"): []} salary_components = {_("Earning"): [], _("Deduction"): []}
@ -93,6 +93,16 @@ def get_conditions(filters):
return conditions, filters return conditions, filters
def get_employee_doj_map():
return frappe._dict(frappe.db.sql("""
SELECT
employee,
date_of_joining
FROM `tabEmployee`
WHERE
`status`='Active'
"""))
def get_ss_earning_map(salary_slips): def get_ss_earning_map(salary_slips):
ss_earnings = frappe.db.sql("""select parent, salary_component, amount ss_earnings = frappe.db.sql("""select parent, salary_component, amount
from `tabSalary Detail` where parent in (%s)""" % from `tabSalary Detail` where parent in (%s)""" %
@ -115,4 +125,4 @@ def get_ss_ded_map(salary_slips):
ss_ded_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, []) ss_ded_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, [])
ss_ded_map[d.parent][d.salary_component] = flt(d.amount) ss_ded_map[d.parent][d.salary_component] = flt(d.amount)
return ss_ded_map return ss_ded_map

View File

@ -568,6 +568,7 @@ def make_stock_entry(production_order_id, purpose, qty=None):
stock_entry.bom_no = production_order.bom_no stock_entry.bom_no = production_order.bom_no
stock_entry.use_multi_level_bom = production_order.use_multi_level_bom stock_entry.use_multi_level_bom = production_order.use_multi_level_bom
stock_entry.fg_completed_qty = qty or (flt(production_order.qty) - flt(production_order.produced_qty)) stock_entry.fg_completed_qty = qty or (flt(production_order.qty) - flt(production_order.produced_qty))
stock_entry.set_stock_entry_type()
if purpose=="Material Transfer for Manufacture": if purpose=="Material Transfer for Manufacture":
stock_entry.to_warehouse = wip_warehouse stock_entry.to_warehouse = wip_warehouse

View File

@ -631,6 +631,7 @@ def make_stock_entry(work_order_id, purpose, qty=None):
additional_costs = get_additional_costs(work_order, fg_qty=stock_entry.fg_completed_qty) additional_costs = get_additional_costs(work_order, fg_qty=stock_entry.fg_completed_qty)
stock_entry.set("additional_costs", additional_costs) stock_entry.set("additional_costs", additional_costs)
stock_entry.set_stock_entry_type()
stock_entry.get_items() stock_entry.get_items()
return stock_entry.as_dict() return stock_entry.as_dict()

View File

@ -586,8 +586,12 @@ execute:frappe.delete_doc('DocType', 'Notification Control')
erpnext.patches.v12_0.set_gst_category erpnext.patches.v12_0.set_gst_category
erpnext.patches.v11_0.remove_barcodes_field_from_copy_fields_to_variants erpnext.patches.v11_0.remove_barcodes_field_from_copy_fields_to_variants
erpnext.patches.v12_0.set_task_status erpnext.patches.v12_0.set_task_status
erpnext.patches.v11_0.make_italian_localization_fields # 01-03-2019 erpnext.patches.v11_0.make_italian_localization_fields # 26-03-2019
erpnext.patches.v12_0.add_item_name_in_work_orders erpnext.patches.v12_0.add_item_name_in_work_orders
erpnext.patches.v12_0.update_pricing_rule_fields erpnext.patches.v12_0.update_pricing_rule_fields
erpnext.patches.v11_1.make_job_card_time_logs erpnext.patches.v11_1.make_job_card_time_logs
erpnext.patches.v12_0.rename_pricing_rule_child_doctypes erpnext.patches.v12_0.rename_pricing_rule_child_doctypes
erpnext.patches.v12_0.move_target_distribution_from_parent_to_child
erpnext.patches.v12_0.stock_entry_enhancements
erpnext.patches.v10_0.item_barcode_childtable_migrate # 16-02-2019
erpnext.patches.v12_0.move_item_tax_to_item_tax_template

View File

@ -6,7 +6,6 @@ from erpnext.regional.italy.setup import make_custom_fields, setup_report
from erpnext.regional.italy import state_codes from erpnext.regional.italy import state_codes
import frappe import frappe
def execute(): def execute():
company = frappe.get_all('Company', filters = {'country': 'Italy'}) company = frappe.get_all('Company', filters = {'country': 'Italy'})
if not company: if not company:
@ -27,4 +26,12 @@ def execute():
frappe.db.sql(""" frappe.db.sql("""
UPDATE tabAddress set {condition} country_code = UPPER(ifnull((select code UPDATE tabAddress set {condition} country_code = UPPER(ifnull((select code
from `tabCountry` where name = `tabAddress`.country), '')) from `tabCountry` where name = `tabAddress`.country), ''))
where country_code is null and state_code is null
""".format(condition=condition)) """.format(condition=condition))
frappe.db.sql("""
UPDATE `tabSales Invoice Item` si, `tabSales Order` so
set si.customer_po_no = so.po_no, si.customer_po_date = so.po_date
WHERE
si.sales_order = so.name and so.po_no is not null
""")

View File

@ -7,13 +7,13 @@ import frappe
def execute(): def execute():
frappe.reload_doc('buying', 'doctype', 'buying_settings') frappe.reload_doc('buying', 'doctype', 'buying_settings')
frappe.db.set_value('Buying Settings', None, 'backflush_raw_materials_of_subcontract_based_on', 'BOM') frappe.db.set_value('Buying Settings', None, 'backflush_raw_materials_of_subcontract_based_on', 'BOM')
frappe.reload_doc('stock', 'doctype', 'stock_entry_detail') frappe.reload_doc('stock', 'doctype', 'stock_entry_detail')
frappe.db.sql(""" update `tabStock Entry Detail` as sed, frappe.db.sql(""" update `tabStock Entry Detail` as sed,
`tabStock Entry` as se, `tabPurchase Order Item Supplied` as pois `tabStock Entry` as se, `tabPurchase Order Item Supplied` as pois
set set
sed.subcontracted_item = pois.main_item_code sed.subcontracted_item = pois.main_item_code
where where
se.purpose = 'Subcontract' and sed.parent = se.name se.purpose = 'Send to Subcontractor' and sed.parent = se.name
and pois.rm_item_code = sed.item_code and se.docstatus = 1 and pois.rm_item_code = sed.item_code and se.docstatus = 1
and pois.parenttype = 'Purchase Order'""") and pois.parenttype = 'Purchase Order'""")

View File

@ -3,6 +3,8 @@ import json
from six import iteritems from six import iteritems
def execute(): def execute():
if "tax_type" not in frappe.db.get_table_columns("Item Tax"):
return
old_item_taxes = {} old_item_taxes = {}
item_tax_templates = {} item_tax_templates = {}
rename_template_to_untitled = [] rename_template_to_untitled = []
@ -40,7 +42,7 @@ def execute():
item.set("taxes", []) item.set("taxes", [])
item.append("taxes", {"item_tax_template": item_tax_template_name, "tax_category": ""}) item.append("taxes", {"item_tax_template": item_tax_template_name, "tax_category": ""})
item.save() item.save()
doctypes = [ doctypes = [
'Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice', 'Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice',
'Supplier Quotation', 'Purchase Order', 'Purchase Receipt', 'Purchase Invoice' 'Supplier Quotation', 'Purchase Order', 'Purchase Receipt', 'Purchase Invoice'

View File

@ -0,0 +1,22 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
frappe.reload_doc("setup", "doctype", "target_detail")
for d in ['Sales Person', 'Sales Partner', 'Territory']:
frappe.db.sql("""
UPDATE `tab{child_doc}`, `tab{parent_doc}`
SET
`tab{child_doc}`.distribution_id = `tab{parent_doc}`.distribution_id
WHERE
`tab{child_doc}`.parent = `tab{parent_doc}`.name
and `tab{parent_doc}`.distribution_id is not null and `tab{parent_doc}`.distribution_id != ''
""".format(parent_doc = d, child_doc = "Target Detail"))
frappe.delete_doc("Report", "Sales Partner-wise Transaction Summary")
frappe.delete_doc("Report", "Sales Person Target Variance Item Group-Wise")
frappe.delete_doc("Report", "Territory Target Variance Item Group-Wise")

View File

@ -0,0 +1,52 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
def execute():
create_stock_entry_types()
company = frappe.db.get_value("Company", {'country': 'India'}, 'name')
if company:
add_gst_hsn_code_field()
def create_stock_entry_types():
frappe.reload_doc('stock', 'doctype', 'stock_entry_type')
frappe.reload_doc('stock', 'doctype', 'stock_entry')
for purpose in ["Material Issue", "Material Receipt", "Material Transfer",
"Material Transfer for Manufacture", "Material Consumption for Manufacture", "Manufacture",
"Repack", "Send to Subcontractor", "Send to Warehouse", "Receive at Warehouse"]:
ste_type = frappe.get_doc({
'doctype': 'Stock Entry Type',
'name': purpose,
'purpose': purpose
})
try:
ste_type.insert()
except frappe.DuplicateEntryError:
pass
frappe.db.sql(" UPDATE `tabStock Entry` set purpose = 'Send to Subcontractor' where purpose = 'Subcontract'")
frappe.db.sql(" UPDATE `tabStock Entry` set stock_entry_type = purpose ")
def add_gst_hsn_code_field():
custom_fields = {
'Stock Entry Detail': [dict(fieldname='gst_hsn_code', label='HSN/SAC',
fieldtype='Data', fetch_from='item_code.gst_hsn_code',
insert_after='description', allow_on_submit=1, print_hide=0)]
}
create_custom_fields(custom_fields, ignore_validate = frappe.flags.in_patch, update=True)
frappe.db.sql(""" update `tabStock Entry Detail`, `tabItem`
SET
`tabStock Entry Detail`.gst_hsn_code = `tabItem`.gst_hsn_code
Where
`tabItem`.name = `tabStock Entry Detail`.item_code and `tabItem`.gst_hsn_code is not null
""")

View File

@ -165,6 +165,13 @@ class Task(NestedSet):
self.update_nsm_model() self.update_nsm_model()
def update_status(self):
if self.status not in ('Cancelled', 'Completed') and self.exp_end_date:
from datetime import datetime
if self.exp_end_date < datetime.now().date():
self.db_set('status', 'Overdue')
self.update_project()
@frappe.whitelist() @frappe.whitelist()
def check_if_child_exists(name): def check_if_child_exists(name):
child_tasks = frappe.get_all("Task", filters={"parent_task": name}) child_tasks = frappe.get_all("Task", filters={"parent_task": name})
@ -196,10 +203,9 @@ def set_multiple_status(names, status):
task.save() task.save()
def set_tasks_as_overdue(): def set_tasks_as_overdue():
frappe.db.sql("""update tabTask set `status`='Overdue' tasks = frappe.get_all("Task", filters={'status':['not in',['Cancelled', 'Completed']]})
where exp_end_date is not null for task in tasks:
and exp_end_date < CURDATE() frappe.get_doc("Task", task.name).update_status()
and `status` not in ('Completed', 'Cancelled')""")
@frappe.whitelist() @frappe.whitelist()
def get_children(doctype, parent, task=None, project=None, is_root=False): def get_children(doctype, parent, task=None, project=None, is_root=False):

View File

@ -10,8 +10,8 @@ frappe.ui.form.on("Timesheet", {
filters:{ filters:{
'status': 'Active' 'status': 'Active'
} }
} };
} };
frm.fields_dict['time_logs'].grid.get_field('task').get_query = function(frm, cdt, cdn) { frm.fields_dict['time_logs'].grid.get_field('task').get_query = function(frm, cdt, cdn) {
var child = locals[cdt][cdn]; var child = locals[cdt][cdn];
@ -20,22 +20,26 @@ frappe.ui.form.on("Timesheet", {
'project': child.project, 'project': child.project,
'status': ["!=", "Cancelled"] 'status': ["!=", "Cancelled"]
} }
} };
} };
frm.fields_dict['time_logs'].grid.get_field('project').get_query = function() { frm.fields_dict['time_logs'].grid.get_field('project').get_query = function() {
return{ return{
filters: { filters: {
'company': frm.doc.company 'company': frm.doc.company
} }
} };
} };
}, },
onload: function(frm){ onload: function(frm){
if (frm.doc.__islocal && frm.doc.time_logs) { if (frm.doc.__islocal && frm.doc.time_logs) {
calculate_time_and_amount(frm); calculate_time_and_amount(frm);
} }
if (frm.is_new()) {
set_employee_and_company(frm);
}
}, },
refresh: function(frm) { refresh: function(frm) {
@ -58,7 +62,7 @@ frappe.ui.form.on("Timesheet", {
if ((row.from_time <= frappe.datetime.now_datetime()) && !row.completed) { if ((row.from_time <= frappe.datetime.now_datetime()) && !row.completed) {
button = 'Resume Timer'; button = 'Resume Timer';
} }
}) });
frm.add_custom_button(__(button), function() { frm.add_custom_button(__(button), function() {
var flag = true; var flag = true;
@ -77,7 +81,7 @@ frappe.ui.form.on("Timesheet", {
erpnext.timesheet.timer(frm, row, timestamp); erpnext.timesheet.timer(frm, row, timestamp);
flag = false; flag = false;
} }
}) });
// If no activities found to start a timer, create new // If no activities found to start a timer, create new
if (flag) { if (flag) {
erpnext.timesheet.timer(frm); erpnext.timesheet.timer(frm);
@ -94,7 +98,7 @@ frappe.ui.form.on("Timesheet", {
frappe.db.get_value('Company', { 'company_name' : frm.doc.company }, 'standard_working_hours') frappe.db.get_value('Company', { 'company_name' : frm.doc.company }, 'standard_working_hours')
.then(({ message }) => { .then(({ message }) => {
(frappe.working_hours = message.standard_working_hours || 0); (frappe.working_hours = message.standard_working_hours || 0);
}); });
}, },
make_invoice: function(frm) { make_invoice: function(frm) {
@ -125,8 +129,8 @@ frappe.ui.form.on("Timesheet", {
frappe.set_route("Form", r.message.doctype, r.message.name); frappe.set_route("Form", r.message.doctype, r.message.name);
} }
} }
}) });
}) });
dialog.show(); dialog.show();
}, },
@ -136,7 +140,7 @@ frappe.ui.form.on("Timesheet", {
frm: frm frm: frm
}); });
}, },
}) });
frappe.ui.form.on("Timesheet Detail", { frappe.ui.form.on("Timesheet Detail", {
time_logs_remove: function(frm) { time_logs_remove: function(frm) {
@ -171,22 +175,22 @@ frappe.ui.form.on("Timesheet Detail", {
.find('[data-fieldname="timer"]') .find('[data-fieldname="timer"]')
.append(frappe.render_template("timesheet")); .append(frappe.render_template("timesheet"));
frm.trigger("control_timer"); frm.trigger("control_timer");
}) });
}, },
hours: function(frm, cdt, cdn) { hours: function(frm, cdt, cdn) {
calculate_end_time(frm, cdt, cdn) calculate_end_time(frm, cdt, cdn);
}, },
billing_hours: function(frm, cdt, cdn) { billing_hours: function(frm, cdt, cdn) {
calculate_billing_costing_amount(frm, cdt, cdn) calculate_billing_costing_amount(frm, cdt, cdn);
}, },
billing_rate: function(frm, cdt, cdn) { billing_rate: function(frm, cdt, cdn) {
calculate_billing_costing_amount(frm, cdt, cdn) calculate_billing_costing_amount(frm, cdt, cdn);
}, },
costing_rate: function(frm, cdt, cdn) { costing_rate: function(frm, cdt, cdn) {
calculate_billing_costing_amount(frm, cdt, cdn) calculate_billing_costing_amount(frm, cdt, cdn);
}, },
billable: function(frm, cdt, cdn) { billable: function(frm, cdt, cdn) {
@ -212,7 +216,7 @@ frappe.ui.form.on("Timesheet Detail", {
calculate_billing_costing_amount(frm, cdt, cdn); calculate_billing_costing_amount(frm, cdt, cdn);
} }
} }
}) });
} }
}); });
@ -240,23 +244,23 @@ var calculate_end_time = function(frm, cdt, cdn) {
frm._setting_hours = true; frm._setting_hours = true;
frappe.model.set_value(cdt, cdn, "to_time", frappe.model.set_value(cdt, cdn, "to_time",
d.format(frappe.defaultDatetimeFormat)).then(() => { d.format(frappe.defaultDatetimeFormat)).then(() => {
frm._setting_hours = false; frm._setting_hours = false;
}); });
} }
} }
} };
var update_billing_hours = function(frm, cdt, cdn){ var update_billing_hours = function(frm, cdt, cdn){
var child = locals[cdt][cdn]; var child = locals[cdt][cdn];
if(!child.billable) frappe.model.set_value(cdt, cdn, 'billing_hours', 0.0); if(!child.billable) frappe.model.set_value(cdt, cdn, 'billing_hours', 0.0);
} };
var update_time_rates = function(frm, cdt, cdn){ var update_time_rates = function(frm, cdt, cdn){
var child = locals[cdt][cdn]; var child = locals[cdt][cdn];
if(!child.billable){ if(!child.billable){
frappe.model.set_value(cdt, cdn, 'billing_rate', 0.0); frappe.model.set_value(cdt, cdn, 'billing_rate', 0.0);
} }
} };
var calculate_billing_costing_amount = function(frm, cdt, cdn){ var calculate_billing_costing_amount = function(frm, cdt, cdn){
var child = locals[cdt][cdn]; var child = locals[cdt][cdn];
@ -270,7 +274,7 @@ var calculate_billing_costing_amount = function(frm, cdt, cdn){
frappe.model.set_value(cdt, cdn, 'billing_amount', billing_amount); frappe.model.set_value(cdt, cdn, 'billing_amount', billing_amount);
frappe.model.set_value(cdt, cdn, 'costing_amount', costing_amount); frappe.model.set_value(cdt, cdn, 'costing_amount', costing_amount);
calculate_time_and_amount(frm); calculate_time_and_amount(frm);
} };
var calculate_time_and_amount = function(frm) { var calculate_time_and_amount = function(frm) {
var tl = frm.doc.time_logs || []; var tl = frm.doc.time_logs || [];
@ -294,4 +298,17 @@ var calculate_time_and_amount = function(frm) {
frm.set_value("total_hours", total_working_hr); frm.set_value("total_hours", total_working_hr);
frm.set_value("total_billable_amount", total_billable_amount); frm.set_value("total_billable_amount", total_billable_amount);
frm.set_value("total_costing_amount", total_costing_amount); frm.set_value("total_costing_amount", total_costing_amount);
} };
// set employee (and company) to the one that's currently logged in
const set_employee_and_company = function(frm) {
const options = { user_id: frappe.session.user };
const fields = ['name', 'company'];
frappe.db.get_value('Employee', options, fields).then(({ message }) => {
if (message) {
// there is an employee with the currently logged in user_id
frm.set_value("employee", message.name);
frm.set_value("company", message.company);
}
});
};

1
erpnext/public/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

View File

@ -158,6 +158,12 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}; };
}); });
} }
if(frappe.meta.get_docfield(this.frm.doc.doctype, "pricing_rules")) {
this.frm.set_indicator_formatter('pricing_rule', function(doc) {
return (doc.rule_applied) ? "green" : "red";
});
}
}, },
onload: function() { onload: function() {
var me = this; var me = this;
@ -422,6 +428,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
method: "erpnext.stock.get_item_details.get_item_details", method: "erpnext.stock.get_item_details.get_item_details",
child: item, child: item,
args: { args: {
doc: me.frm.doc,
args: { args: {
item_code: item.item_code, item_code: item.item_code,
barcode: item.barcode, barcode: item.barcode,
@ -456,7 +463,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
cost_center: item.cost_center, cost_center: item.cost_center,
tax_category: me.frm.doc.tax_category, tax_category: me.frm.doc.tax_category,
item_tax_template: item.item_tax_template, item_tax_template: item.item_tax_template,
child_docname: item.name child_docname: item.name,
} }
}, },
@ -482,7 +489,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
} }
}, },
() => me.conversion_factor(doc, cdt, cdn, true), () => me.conversion_factor(doc, cdt, cdn, true),
() => me.update_free_items(item) () => me.validate_pricing_rule(item)
]); ]);
} }
} }
@ -1116,7 +1123,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
callback: function(r) { callback: function(r) {
if (!r.exc && r.message) { if (!r.exc && r.message) {
r.message.forEach(row_item => { r.message.forEach(row_item => {
me.update_free_items(row_item); me.validate_pricing_rule(row_item);
}); });
me._set_values_for_item_list(r.message); me._set_values_for_item_list(r.message);
me.calculate_taxes_and_totals(); me.calculate_taxes_and_totals();
@ -1139,14 +1146,12 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
return this.frm.call({ return this.frm.call({
method: "erpnext.accounts.doctype.pricing_rule.pricing_rule.apply_pricing_rule", method: "erpnext.accounts.doctype.pricing_rule.pricing_rule.apply_pricing_rule",
args: { args: args }, args: { args: args, doc: me.frm.doc },
callback: function(r) { callback: function(r) {
if (!r.exc && r.message) { if (!r.exc && r.message) {
me._set_values_for_item_list(r.message); me._set_values_for_item_list(r.message);
if(item) me.set_gross_profit(item); if(item) me.set_gross_profit(item);
if(calculate_taxes_and_totals) me.calculate_taxes_and_totals();
if(me.frm.doc.apply_discount_on) me.frm.trigger("apply_discount_on") if(me.frm.doc.apply_discount_on) me.frm.trigger("apply_discount_on")
me.update_free_items(item);
} }
} }
}); });
@ -1200,7 +1205,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
"warehouse": d.warehouse, "warehouse": d.warehouse,
"serial_no": d.serial_no, "serial_no": d.serial_no,
"price_list_rate": d.price_list_rate, "price_list_rate": d.price_list_rate,
"discount_percentage": d.discount_percentage || 0.0,
"conversion_factor": d.conversion_factor || 1.0 "conversion_factor": d.conversion_factor || 1.0
}); });
@ -1241,6 +1245,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
// if pricing rule set as blank from an existing value, apply price_list // if pricing rule set as blank from an existing value, apply price_list
if(!me.frm.doc.ignore_pricing_rule && existing_pricing_rule && !d.pricing_rules) { if(!me.frm.doc.ignore_pricing_rule && existing_pricing_rule && !d.pricing_rules) {
me.apply_price_list(frappe.get_doc(d.doctype, d.name)); me.apply_price_list(frappe.get_doc(d.doctype, d.name));
} else {
me.validate_pricing_rule(frappe.get_doc(d.doctype, d.name));
} }
} }
@ -1288,41 +1294,29 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}); });
}, },
update_free_items: function(item) { validate_pricing_rule: function(item) {
var me = this; let me = this;
const fields = ["discount_percentage", "discount_amount", "pricing_rules"];
if (item.pricing_rules) { if (item.pricing_rules) {
frappe.call({ frappe.call({
method: "erpnext.accounts.doctype.pricing_rule.pricing_rule.get_free_items", method: "erpnext.accounts.doctype.pricing_rule.utils.validate_pricing_rule_for_different_cond",
args: { args: {
pricing_rules: item.pricing_rules, doc: me.frm.doc
item_row: item
}, },
callback: function(r) { callback: function(r) {
let items = []; if (r.message) {
let child = ''; r.message.items.forEach(d => {
me.frm.doc.items.forEach(row => {
me.frm.doc.items.map(d => { if(d.name == row.name) {
items[d.item_code] = d; fields.forEach(f => {
}); row[f] = d[f];
});
if(r.message && r.message.length) { }
r.message.forEach(d => {
// If free item is already exists
if(d.item_code in items &&
d.is_free_item && items[d.item_code].is_free_item) {
child = items[d.item_code];
} else {
child = frappe.model.add_child(me.frm.doc, item.doctype, "items");
}
$.each(d, function(k, v) {
child[k] = v;
}); });
me.frm.script_manager.trigger("price_list_rate", child.doctype, child.name);
}); });
me.trigger_price_list_rate();
} }
} }
}); });
@ -1337,9 +1331,29 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
me.frm.doc.items = items; me.frm.doc.items = items;
refresh_field('items'); refresh_field('items');
} else if(item.applied_on_items && item.apply_on) {
const applied_on_items = item.applied_on_items.split(',');
me.frm.doc.items.forEach(row => {
if(applied_on_items.includes(row[item.apply_on])) {
fields.forEach(f => {
row[f] = 0;
});
}
});
me.trigger_price_list_rate();
} }
}, },
trigger_price_list_rate: function() {
var me = this;
this.frm.doc.items.forEach(child_row => {
me.frm.script_manager.trigger("price_list_rate",
child_row.doctype, child_row.name);
})
},
validate_company_and_party: function() { validate_company_and_party: function() {
var me = this; var me = this;
var valid = true; var valid = true;
@ -1379,7 +1393,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}, },
callback: function(r) { callback: function(r) {
if(!r.exc) { if(!r.exc) {
me.frm.set_value("taxes", r.message); for (let tax of r.message) {
me.frm.add_child("taxes", tax);
}
me.calculate_taxes_and_totals(); me.calculate_taxes_and_totals();
} }
} }

View File

@ -31,7 +31,7 @@
<base-image :src="image" :alt="title" /> <base-image :src="image" :alt="title" />
</div> </div>
</div> </div>
<div class="col-md-8"> <div class="col-md-8" style='padding-left: 30px;'>
<h2>{{ title }}</h2> <h2>{{ title }}</h2>
<div class="text-muted"> <div class="text-muted">
<slot name="detail-header-item"></slot> <slot name="detail-header-item"></slot>

View File

@ -1 +0,0 @@
/Users/netchampfaris/frappe-bench/apps/erpnext/node_modules

View File

@ -5,6 +5,7 @@ from __future__ import unicode_literals
import frappe import frappe
import unittest import unittest
from frappe.utils import getdate
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.item.test_item import make_item
@ -12,10 +13,27 @@ import json
class TestGSTR3BReport(unittest.TestCase): class TestGSTR3BReport(unittest.TestCase):
def test_gstr_3b_report(self): def test_gstr_3b_report(self):
month_number_mapping = {
1: "January",
2: "February",
3: "March",
4: "April",
5: "May",
6: "June",
7: "July",
8: "August",
9: "September",
10: "October",
11: "November",
12: "December"
}
frappe.set_user("Administrator") frappe.set_user("Administrator")
frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company GST'") frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company GST'")
frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company GST'") frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company GST'")
frappe.db.sql("delete from `tabGSTR 3B Report` where company='_Test Company GST'")
make_company() make_company()
make_item("Milk", properties = {"is_nil_exempt": 1, "standard_rate": 0.000000}) make_item("Milk", properties = {"is_nil_exempt": 1, "standard_rate": 0.000000})
@ -33,8 +51,8 @@ class TestGSTR3BReport(unittest.TestCase):
"doctype": "GSTR 3B Report", "doctype": "GSTR 3B Report",
"company": "_Test Company GST", "company": "_Test Company GST",
"company_address": "_Test Address-Billing", "company_address": "_Test Address-Billing",
"year": "2019", "year": getdate().year,
"month": "March" "month": month_number_mapping.get(getdate().month)
}).insert() }).insert()
output = json.loads(report.json_output) output = json.loads(report.json_output)
@ -55,7 +73,6 @@ def make_sales_invoice():
income_account = 'Sales - _GST', income_account = 'Sales - _GST',
expense_account = 'Cost of Goods Sold - _GST', expense_account = 'Cost of Goods Sold - _GST',
cost_center = 'Main - _GST', cost_center = 'Main - _GST',
posting_date = '2019-03-10',
do_not_save=1 do_not_save=1
) )
@ -77,7 +94,6 @@ def make_sales_invoice():
income_account = 'Sales - _GST', income_account = 'Sales - _GST',
expense_account = 'Cost of Goods Sold - _GST', expense_account = 'Cost of Goods Sold - _GST',
cost_center = 'Main - _GST', cost_center = 'Main - _GST',
posting_date = '2019-03-10',
do_not_save=1 do_not_save=1
) )
@ -99,7 +115,6 @@ def make_sales_invoice():
income_account = 'Sales - _GST', income_account = 'Sales - _GST',
expense_account = 'Cost of Goods Sold - _GST', expense_account = 'Cost of Goods Sold - _GST',
cost_center = 'Main - _GST', cost_center = 'Main - _GST',
posting_date = '2019-03-10',
do_not_save=1 do_not_save=1
) )
@ -122,7 +137,6 @@ def make_sales_invoice():
income_account = 'Sales - _GST', income_account = 'Sales - _GST',
expense_account = 'Cost of Goods Sold - _GST', expense_account = 'Cost of Goods Sold - _GST',
cost_center = 'Main - _GST', cost_center = 'Main - _GST',
posting_date = '2019-03-10',
do_not_save=1 do_not_save=1
) )
si3.submit() si3.submit()
@ -135,7 +149,6 @@ def create_purchase_invoices():
currency = 'INR', currency = 'INR',
warehouse = 'Finished Goods - _GST', warehouse = 'Finished Goods - _GST',
cost_center = 'Main - _GST', cost_center = 'Main - _GST',
posting_date = '2019-03-10',
do_not_save=1, do_not_save=1,
) )
@ -157,7 +170,6 @@ def create_purchase_invoices():
currency = 'INR', currency = 'INR',
warehouse = 'Finished Goods - _GST', warehouse = 'Finished Goods - _GST',
cost_center = 'Main - _GST', cost_center = 'Main - _GST',
posting_date = '2019-03-10',
item = "Milk", item = "Milk",
do_not_save=1 do_not_save=1
) )

View File

@ -265,6 +265,7 @@ def make_custom_fields(update=True):
'Purchase Order Item': [hsn_sac_field, nil_rated_exempt, is_non_gst], 'Purchase Order Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
'Purchase Receipt Item': [hsn_sac_field, nil_rated_exempt, is_non_gst], 'Purchase Receipt Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
'Purchase Invoice Item': [hsn_sac_field, nil_rated_exempt, is_non_gst], 'Purchase Invoice Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
'Material Request Item': [hsn_sac_field, nil_rated_exempt, is_non_gst],
'Employee': [ 'Employee': [
dict(fieldname='ifsc_code', label='IFSC Code', dict(fieldname='ifsc_code', label='IFSC Code',
fieldtype='Data', insert_after='bank_ac_no', print_hide=1, fieldtype='Data', insert_after='bank_ac_no', print_hide=1,

View File

@ -95,13 +95,12 @@
<Cognome>{{ doc.customer_data.last_name }}</Cognome> <Cognome>{{ doc.customer_data.last_name }}</Cognome>
</Anagrafica> </Anagrafica>
{%- else %} {%- else %}
{%- if doc.customer_data.is_public_administration %}
<CodiceFiscale>{{ doc.customer_data.fiscal_code }}</CodiceFiscale>
{%- else %}
<IdFiscaleIVA> <IdFiscaleIVA>
<IdPaese>{{ doc.customer_address_data.country_code }}</IdPaese> <IdPaese>{{ doc.customer_address_data.country_code }}</IdPaese>
<IdCodice>{{ doc.tax_id | replace("IT","") }}</IdCodice> <IdCodice>{{ doc.tax_id | replace("IT","") }}</IdCodice>
</IdFiscaleIVA> </IdFiscaleIVA>
{%- if doc.customer_data.fiscal_code %}
<CodiceFiscale>{{ doc.customer_data.fiscal_code }}</CodiceFiscale>
{%- endif %} {%- endif %}
<Anagrafica> <Anagrafica>
<Denominazione>{{ doc.customer_name }}</Denominazione> <Denominazione>{{ doc.customer_name }}</Denominazione>
@ -128,22 +127,42 @@
<ImportoBollo>{{ format_float(doc.stamp_duty) }}</ImportoBollo> <ImportoBollo>{{ format_float(doc.stamp_duty) }}</ImportoBollo>
</DatiBollo> </DatiBollo>
{%- endif %} {%- endif %}
<ImportoTotaleDocumento>{{ format_float(doc.grand_total) }}</ImportoTotaleDocumento> {%- if doc.discount_amount %}
<ScontoMaggiorazione>
{%- if doc.discount_amount > 0.0 %}
<Tipo>SC</Tipo>
{%- else %}
<Tipo>MG</Tipo>
{%- endif %}
{%- if doc.additional_discount_percentage > 0.0 %}
<Percentuale>{{ format_float(doc.additional_discount_percentage) }}</Percentuale>
{%- endif %}
<Importo>{{ format_float(doc.discount_amount) }}</Importo>
</ScontoMaggiorazione>
{%- endif %}
<ImportoTotaleDocumento>{{ format_float(doc.rounded_total or doc.grand_total) }}</ImportoTotaleDocumento>
<Causale>VENDITA</Causale> <Causale>VENDITA</Causale>
</DatiGeneraliDocumento> </DatiGeneraliDocumento>
{%- if doc.po_no %} {%- for po_no, po_date in doc.customer_po_data.items() %}
<DatiOrdineAcquisto> <DatiOrdineAcquisto>
<IdDocumento>{{ doc.po_no }}</IdDocumento> <IdDocumento>{{ po_no }}</IdDocumento>
{%- if doc.po_date %} <Data>{{ po_date }}</Data>
<Data>{{ doc.po_date }}</Data> </DatiOrdineAcquisto>
{%- endif %} {%- endfor %}
</DatiOrdineAcquisto>
{%- endif %}
{%- if doc.is_return and doc.return_against_unamended %} {%- if doc.is_return and doc.return_against_unamended %}
<DatiFattureCollegate> <DatiFattureCollegate>
<IdDocumento>{{ doc.return_against_unamended }}</IdDocumento> <IdDocumento>{{ doc.return_against_unamended }}</IdDocumento>
</DatiFattureCollegate> </DatiFattureCollegate>
{%- endif %} {%- endif %}
{%- for row in doc.e_invoice_items %}
{%- if row.delivery_note %}
<DatiDDT>
<NumeroDDT>{{ row.delivery_note }}</NumeroDDT>
<DataDDT>{{ frappe.db.get_value('Delivery Note', row.delivery_note, 'posting_date') }}</DataDDT>
<RiferimentoNumeroLinea>{{ row.idx }}</RiferimentoNumeroLinea>
</DatiDDT>
{%- endif %}
{%- endfor %}
{%- if doc.shipping_address_data %} {%- if doc.shipping_address_data %}
<DatiTrasporto> <DatiTrasporto>
<IndirizzoResa> <IndirizzoResa>
@ -165,7 +184,11 @@
<UnitaMisura>{{ item.stock_uom }}</UnitaMisura> <UnitaMisura>{{ item.stock_uom }}</UnitaMisura>
<PrezzoUnitario>{{ format_float(item.price_list_rate or item.rate) }}</PrezzoUnitario> <PrezzoUnitario>{{ format_float(item.price_list_rate or item.rate) }}</PrezzoUnitario>
{{ render_discount_or_margin(item) }} {{ render_discount_or_margin(item) }}
<PrezzoTotale>{{ format_float(item.amount) }}</PrezzoTotale> {%- if (item.discount_amount or item.rate_with_margin) %}
<PrezzoTotale>{{ format_float(item.net_amount) }}</PrezzoTotale>
{%- else %}
<PrezzoTotale>{{ format_float(item.amount) }}</PrezzoTotale>
{%- endif %}
<AliquotaIVA>{{ format_float(item.tax_rate) }}</AliquotaIVA> <AliquotaIVA>{{ format_float(item.tax_rate) }}</AliquotaIVA>
{%- if item.tax_exemption_reason %} {%- if item.tax_exemption_reason %}
<Natura>{{ item.tax_exemption_reason.split("-")[0] }}</Natura> <Natura>{{ item.tax_exemption_reason.split("-")[0] }}</Natura>
@ -199,7 +222,9 @@
<ModalitaPagamento>{{ payment_term.mode_of_payment_code.split("-")[0] }}</ModalitaPagamento> <ModalitaPagamento>{{ payment_term.mode_of_payment_code.split("-")[0] }}</ModalitaPagamento>
<DataScadenzaPagamento>{{ payment_term.due_date }}</DataScadenzaPagamento> <DataScadenzaPagamento>{{ payment_term.due_date }}</DataScadenzaPagamento>
<ImportoPagamento>{{ format_float(payment_term.payment_amount) }}</ImportoPagamento> <ImportoPagamento>{{ format_float(payment_term.payment_amount) }}</ImportoPagamento>
<IstitutoFinanziario>{{ payment_term.bank_account_name }}</IstitutoFinanziario> {%- if payment_term.bank_account_name %}
<IstitutoFinanziario>{{ payment_term.bank_account_name }}</IstitutoFinanziario>
{%- endif %}
{%- if payment_term.bank_account_iban %} {%- if payment_term.bank_account_iban %}
<IBAN>{{ payment_term.bank_account_iban }}</IBAN> <IBAN>{{ payment_term.bank_account_iban }}</IBAN>
<ABI>{{ payment_term.bank_account_iban[5:10] }}</ABI> <ABI>{{ payment_term.bank_account_iban[5:10] }}</ABI>

View File

@ -3,15 +3,26 @@ erpnext.setup_e_invoice_button = (doctype) => {
refresh: (frm) => { refresh: (frm) => {
if(frm.doc.docstatus == 1) { if(frm.doc.docstatus == 1) {
frm.add_custom_button('Generate E-Invoice', () => { frm.add_custom_button('Generate E-Invoice', () => {
var w = window.open( frm.call({
frappe.urllib.get_full_url( method: "erpnext.regional.italy.utils.generate_single_invoice",
"/api/method/erpnext.regional.italy.utils.generate_single_invoice?" args: {
+ "docname=" + frm.doc.name docname: frm.doc.name
) },
) callback: function(r) {
if (!w) { frm.reload_doc();
frappe.msgprint(__("Please enable pop-ups")); return; if(r.message) {
} var w = window.open(
frappe.urllib.get_full_url(
"/api/method/erpnext.regional.italy.utils.download_e_invoice_file?"
+ "file_name=" + r.message
)
)
if (!w) {
frappe.msgprint(__("Please enable pop-ups")); return;
}
}
}
});
}); });
} }
} }

View File

@ -26,6 +26,22 @@ def make_custom_fields(update=True):
print_hide=1, hidden=1, read_only=1, options="currency") print_hide=1, hidden=1, read_only=1, options="currency")
] ]
customer_po_fields = [
dict(fieldname='customer_po_details', label='Customer PO',
fieldtype='Section Break', insert_after='image'),
dict(fieldname='customer_po_no', label='Customer PO No',
fieldtype='Data', insert_after='customer_po_details',
fetch_from = 'sales_order.po_no',
print_hide=1, allow_on_submit=1, fetch_if_empty= 1, read_only=1, no_copy=1),
dict(fieldname='customer_po_clm_brk', label='',
fieldtype='Column Break', insert_after='customer_po_no',
print_hide=1, read_only=1),
dict(fieldname='customer_po_date', label='Customer PO Date',
fieldtype='Date', insert_after='customer_po_clm_brk',
fetch_from = 'sales_order.po_date',
print_hide=1, allow_on_submit=1, fetch_if_empty= 1, read_only=1, no_copy=1)
]
custom_fields = { custom_fields = {
'Company': [ 'Company': [
dict(fieldname='sb_e_invoicing', label='E-Invoicing', dict(fieldname='sb_e_invoicing', label='E-Invoicing',
@ -128,7 +144,7 @@ def make_custom_fields(update=True):
'Purchase Invoice Item': invoice_item_fields, 'Purchase Invoice Item': invoice_item_fields,
'Sales Order Item': invoice_item_fields, 'Sales Order Item': invoice_item_fields,
'Delivery Note Item': invoice_item_fields, 'Delivery Note Item': invoice_item_fields,
'Sales Invoice Item': invoice_item_fields, 'Sales Invoice Item': invoice_item_fields + customer_po_fields,
'Quotation Item': invoice_item_fields, 'Quotation Item': invoice_item_fields,
'Purchase Order Item': invoice_item_fields, 'Purchase Order Item': invoice_item_fields,
'Purchase Receipt Item': invoice_item_fields, 'Purchase Receipt Item': invoice_item_fields,

View File

@ -5,6 +5,7 @@ from frappe.utils import flt, cstr
from erpnext.controllers.taxes_and_totals import get_itemised_tax from erpnext.controllers.taxes_and_totals import get_itemised_tax
from frappe import _ from frappe import _
from frappe.core.doctype.file.file import remove_file from frappe.core.doctype.file.file import remove_file
from six import string_types
from frappe.desk.form.load import get_attachments from frappe.desk.form.load import get_attachments
from erpnext.regional.italy import state_codes from erpnext.regional.italy import state_codes
@ -82,6 +83,14 @@ def prepare_invoice(invoice, progressive_number):
if item.tax_rate == 0.0 and item.tax_amount == 0.0: if item.tax_rate == 0.0 and item.tax_amount == 0.0:
item.tax_exemption_reason = tax_data["0.0"]["tax_exemption_reason"] item.tax_exemption_reason = tax_data["0.0"]["tax_exemption_reason"]
customer_po_data = {}
for d in invoice.e_invoice_items:
if (d.customer_po_no and d.customer_po_date
and d.customer_po_no not in customer_po_data):
customer_po_data[d.customer_po_no] = d.customer_po_date
invoice.customer_po_data = customer_po_data
return invoice return invoice
def get_conditions(filters): def get_conditions(filters):
@ -134,6 +143,7 @@ def get_invoice_summary(items, taxes):
idx=len(items)+1, idx=len(items)+1,
item_code=reference_row.description, item_code=reference_row.description,
item_name=reference_row.description, item_name=reference_row.description,
description=reference_row.description,
rate=reference_row.tax_amount, rate=reference_row.tax_amount,
qty=1.0, qty=1.0,
amount=reference_row.tax_amount, amount=reference_row.tax_amount,
@ -142,7 +152,7 @@ def get_invoice_summary(items, taxes):
tax_amount=(reference_row.tax_amount * tax.rate) / 100, tax_amount=(reference_row.tax_amount * tax.rate) / 100,
net_amount=reference_row.tax_amount, net_amount=reference_row.tax_amount,
taxable_amount=reference_row.tax_amount, taxable_amount=reference_row.tax_amount,
item_tax_rate="{}", item_tax_rate={tax.account_head: tax.rate},
charges=True charges=True
) )
) )
@ -150,10 +160,16 @@ def get_invoice_summary(items, taxes):
#Check item tax rates if tax rate is zero. #Check item tax rates if tax rate is zero.
if tax.rate == 0: if tax.rate == 0:
for item in items: for item in items:
item_tax_rate = json.loads(item.item_tax_rate) item_tax_rate = item.item_tax_rate
if tax.account_head in item_tax_rate: if isinstance(item.item_tax_rate, string_types):
item_tax_rate = json.loads(item.item_tax_rate)
if item_tax_rate and tax.account_head in item_tax_rate:
key = cstr(item_tax_rate[tax.account_head]) key = cstr(item_tax_rate[tax.account_head])
summary_data.setdefault(key, {"tax_amount": 0.0, "taxable_amount": 0.0, "tax_exemption_reason": "", "tax_exemption_law": ""}) if key not in summary_data:
summary_data.setdefault(key, {"tax_amount": 0.0, "taxable_amount": 0.0,
"tax_exemption_reason": "", "tax_exemption_law": ""})
summary_data[key]["tax_amount"] += item.tax_amount summary_data[key]["tax_amount"] += item.tax_amount
summary_data[key]["taxable_amount"] += item.net_amount summary_data[key]["taxable_amount"] += item.net_amount
if key == "0.0": if key == "0.0":
@ -198,19 +214,25 @@ def sales_invoice_validate(doc):
else: else:
doc.company_fiscal_regime = company_fiscal_regime doc.company_fiscal_regime = company_fiscal_regime
doc.company_tax_id = frappe.get_cached_value("Company", doc.company, 'tax_id')
doc.company_fiscal_code = frappe.get_cached_value("Company", doc.company, 'fiscal_code')
if not doc.company_tax_id and not doc.company_fiscal_code: if not doc.company_tax_id and not doc.company_fiscal_code:
frappe.throw(_("Please set either the Tax ID or Fiscal Code on Company '%s'" % doc.company), title=_("E-Invoicing Information Missing")) frappe.throw(_("Please set either the Tax ID or Fiscal Code on Company '%s'" % doc.company), title=_("E-Invoicing Information Missing"))
#Validate customer details #Validate customer details
customer_type, is_public_administration = frappe.db.get_value("Customer", doc.customer, ["customer_type", "is_public_administration"]) customer = frappe.get_doc("Customer", doc.customer)
if customer_type == _("Individual"):
if customer.customer_type == _("Individual"):
doc.customer_fiscal_code = customer.fiscal_code
if not doc.customer_fiscal_code: if not doc.customer_fiscal_code:
frappe.throw(_("Please set Fiscal Code for the customer '%s'" % doc.customer), title=_("E-Invoicing Information Missing")) frappe.throw(_("Please set Fiscal Code for the customer '%s'" % doc.customer), title=_("E-Invoicing Information Missing"))
else: else:
if is_public_administration: if customer.is_public_administration:
doc.customer_fiscal_code = customer.fiscal_code
if not doc.customer_fiscal_code: if not doc.customer_fiscal_code:
frappe.throw(_("Please set Fiscal Code for the public administration '%s'" % doc.customer), title=_("E-Invoicing Information Missing")) frappe.throw(_("Please set Fiscal Code for the public administration '%s'" % doc.customer), title=_("E-Invoicing Information Missing"))
else: else:
doc.tax_id = customer.tax_id
if not doc.tax_id: if not doc.tax_id:
frappe.throw(_("Please set Tax ID for the customer '%s'" % doc.customer), title=_("E-Invoicing Information Missing")) frappe.throw(_("Please set Tax ID for the customer '%s'" % doc.customer), title=_("E-Invoicing Information Missing"))
@ -276,13 +298,18 @@ def prepare_and_attach_invoice(doc, replace=False):
def generate_single_invoice(docname): def generate_single_invoice(docname):
doc = frappe.get_doc("Sales Invoice", docname) doc = frappe.get_doc("Sales Invoice", docname)
e_invoice = prepare_and_attach_invoice(doc, True) e_invoice = prepare_and_attach_invoice(doc, True)
return e_invoice.file_name
@frappe.whitelist()
def download_e_invoice_file(file_name):
content = None content = None
with open(frappe.get_site_path('private', 'files', e_invoice.file_name), "r") as f: with open(frappe.get_site_path('private', 'files', file_name), "r") as f:
content = f.read() content = f.read()
frappe.local.response.filename = e_invoice.file_name frappe.local.response.filename = file_name
frappe.local.response.filecontent = content frappe.local.response.filecontent = content
frappe.local.response.type = "download" frappe.local.response.type = "download"

View File

@ -4,7 +4,7 @@
frappe.query_reports["GSTR-1"] = { frappe.query_reports["GSTR-1"] = {
"filters": [ "filters": [
{ {
"fieldname":"company", "fieldname": "company",
"label": __("Company"), "label": __("Company"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Company", "options": "Company",
@ -12,22 +12,22 @@ frappe.query_reports["GSTR-1"] = {
"default": frappe.defaults.get_user_default("Company") "default": frappe.defaults.get_user_default("Company")
}, },
{ {
"fieldname":"company_address", "fieldname": "company_address",
"label": __("Address"), "label": __("Address"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Address", "options": "Address",
"get_query": function() { "get_query": function () {
var company = frappe.query_report.get_filter_value('company'); var company = frappe.query_report.get_filter_value('company');
if (company) { if (company) {
return { return {
"query": 'frappe.contacts.doctype.address.address.address_query', "query": 'frappe.contacts.doctype.address.address.address_query',
"filters": { link_doctype: 'Company', link_name: company} "filters": { link_doctype: 'Company', link_name: company }
}; };
} }
} }
}, },
{ {
"fieldname":"from_date", "fieldname": "from_date",
"label": __("From Date"), "label": __("From Date"),
"fieldtype": "Date", "fieldtype": "Date",
"reqd": 1, "reqd": 1,
@ -35,19 +35,34 @@ frappe.query_reports["GSTR-1"] = {
"width": "80" "width": "80"
}, },
{ {
"fieldname":"to_date", "fieldname": "to_date",
"label": __("To Date"), "label": __("To Date"),
"fieldtype": "Date", "fieldtype": "Date",
"reqd": 1, "reqd": 1,
"default": frappe.datetime.get_today() "default": frappe.datetime.get_today()
}, },
{ {
"fieldname":"type_of_business", "fieldname": "type_of_business",
"label": __("Type of Business"), "label": __("Type of Business"),
"fieldtype": "Select", "fieldtype": "Select",
"reqd": 1, "reqd": 1,
"options": ["B2B", "B2C Large", "B2C Small","CDNR", "EXPORT"], "options": ["B2B", "B2C Large", "B2C Small", "CDNR", "EXPORT"],
"default": "B2B" "default": "B2B"
} }
] ],
onload: function (report) {
report.page.add_inner_button(__("Download as Json"), function () {
var filters = report.get_values();
const args = {
cmd: 'erpnext.regional.report.gstr_1.gstr_1.get_json',
data: report.data,
report_name: report.report_name,
filters: filters
};
open_url_post(frappe.request.url, args);
});
}
} }

View File

@ -4,9 +4,10 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe, json import frappe, json
from frappe import _ from frappe import _
from frappe.utils import flt, formatdate from frappe.utils import flt, formatdate, now_datetime, getdate
from datetime import date from datetime import date
from six import iteritems from six import iteritems
from erpnext.regional.doctype.gstr_3b_report.gstr_3b_report import get_period
def execute(filters=None): def execute(filters=None):
return Gstr1Report(filters).run() return Gstr1Report(filters).run()
@ -38,7 +39,7 @@ class Gstr1Report(object):
shipping_bill_date, shipping_bill_date,
reason_for_issuing_document reason_for_issuing_document
""" """
self.customer_type = "Company" if self.filters.get("type_of_business") == "B2B" else "Individual" # self.customer_type = "Company" if self.filters.get("type_of_business") == "B2B" else "Individual"
def run(self): def run(self):
self.get_columns() self.get_columns()
@ -113,9 +114,14 @@ class Gstr1Report(object):
if self.filters.get(opts[0]): if self.filters.get(opts[0]):
conditions += opts[1] conditions += opts[1]
customers = frappe.get_all("Customer", filters={"customer_type": self.customer_type}) # customers = frappe.get_all("Customer", filters={"customer_type": self.customer_type})
if self.filters.get("type_of_business") == "B2B": if self.filters.get("type_of_business") == "B2B":
customers = frappe.get_all("Customer",
filters={
"gst_category": ["in", ["Registered Regular", "Deemed Export", "SEZ"]]
})
conditions += """ and ifnull(gst_category, '') != 'Overseas' and is_return != 1 conditions += """ and ifnull(gst_category, '') != 'Overseas' and is_return != 1
and customer in ({0})""".format(", ".join([frappe.db.escape(c.name) for c in customers])) and customer in ({0})""".format(", ".join([frappe.db.escape(c.name) for c in customers]))
@ -124,6 +130,11 @@ class Gstr1Report(object):
if not b2c_limit: if not b2c_limit:
frappe.throw(_("Please set B2C Limit in GST Settings.")) frappe.throw(_("Please set B2C Limit in GST Settings."))
customers = frappe.get_all("Customer",
filters={
"gst_category": ["in", ["Unregistered"]]
})
if self.filters.get("type_of_business") == "B2C Large": if self.filters.get("type_of_business") == "B2C Large":
conditions += """ and SUBSTR(place_of_supply, 1, 2) != SUBSTR(company_gstin, 1, 2) conditions += """ and SUBSTR(place_of_supply, 1, 2) != SUBSTR(company_gstin, 1, 2)
and grand_total > {0} and is_return != 1 and customer in ({1})""".\ and grand_total > {0} and is_return != 1 and customer in ({1})""".\
@ -494,3 +505,158 @@ class Gstr1Report(object):
} }
] ]
self.columns = self.invoice_columns + self.tax_columns + self.other_columns self.columns = self.invoice_columns + self.tax_columns + self.other_columns
@frappe.whitelist()
def get_json():
data = frappe._dict(frappe.local.form_dict)
del data["cmd"]
if "csrf_token" in data:
del data["csrf_token"]
filters = json.loads(data["filters"])
report_data = json.loads(data["data"])
report_name = data["report_name"]
gstin = get_company_gstin_number(filters["company"])
fp = "%02d%s" % (getdate(filters["to_date"]).month, getdate(filters["to_date"]).year)
gst_json = {"gstin": "", "version": "GST2.2.9",
"hash": "hash", "gstin": gstin, "fp": fp}
res = {}
if filters["type_of_business"] == "B2B":
for item in report_data:
res.setdefault(item["customer_gstin"], {}).setdefault(item["invoice_number"],[]).append(item)
out = get_b2b_json(res, gstin)
gst_json["b2b"] = out
elif filters["type_of_business"] == "B2C Large":
for item in report_data:
res.setdefault(item["place_of_supply"], []).append(item)
out = get_b2cl_json(res, gstin)
gst_json["b2cl"] = out
elif filters["type_of_business"] == "EXPORT":
for item in report_data:
res.setdefault(item["export_type"], []).append(item)
out = get_export_json(res)
gst_json["exp"] = out
download_json_file(report_name, filters["type_of_business"], gst_json)
def get_b2b_json(res, gstin):
inv_type, out = {"Registered Regular": "R", "Deemed Export": "DE", "URD": "URD", "SEZ": "SEZ"}, []
for gst_in in res:
b2b_item, inv = {"ctin": gst_in, "inv": []}, []
if not gst_in: continue
for number, invoice in iteritems(res[gst_in]):
inv_item = get_basic_invoice_detail(invoice[0])
inv_item["pos"] = "%02d" % int(invoice[0]["place_of_supply"].split('-')[0])
inv_item["rchrg"] = invoice[0]["reverse_charge"]
inv_item["inv_typ"] = inv_type.get(invoice[0].get("gst_category", ""),"")
if inv_item["pos"]=="00": continue
inv_item["itms"] = []
for item in invoice:
inv_item["itms"].append(get_rate_and_tax_details(item, gstin))
inv.append(inv_item)
if not inv: continue
b2b_item["inv"] = inv
out.append(b2b_item)
return out
def get_b2cl_json(res, gstin):
out = []
for pos in res:
b2cl_item, inv = {"pos": "%02d" % int(pos.split('-')[0]), "inv": []}, []
for row in res[pos]:
inv_item = get_basic_invoice_detail(row)
if row.get("sale_from_bonded_wh"):
inv_item["inv_typ"] = "CBW"
inv_item["itms"] = [get_rate_and_tax_details(row, gstin)]
inv.append(inv_item)
b2cl_item["inv"] = inv
out.append(b2cl_item)
return out
def get_export_json(res):
out = []
for exp_type in res:
exp_item, inv = {"exp_typ": exp_type, "inv": []}, []
for row in res[exp_type]:
inv_item = get_basic_invoice_detail(row)
inv_item["itms"] = [{
"txval": flt(row["taxable_value"], 2),
"rt": row["rate"] or 0,
"iamt": 0,
"csamt": 0
}]
inv.append(inv_item)
exp_item["inv"] = inv
out.append(exp_item)
return out
def get_basic_invoice_detail(row):
return {
"inum": row["invoice_number"],
"idt": getdate(row["posting_date"]).strftime('%d-%m-%Y'),
"val": flt(row["invoice_value"], 2)
}
def get_rate_and_tax_details(row, gstin):
itm_det = {"txval": flt(row["taxable_value"], 2),
"rt": row["rate"],
"csamt": (flt(row.get("cess_amount"), 2) or 0)
}
# calculate rate
num = 1 if not row["rate"] else "%d%02d" % (row["rate"], 1)
rate = row.get("rate") or 0
# calculate tax amount added
tax = flt((row["taxable_value"]*rate)/100.0, 2)
frappe.errprint([tax, tax/2])
if row.get("customer_gstin") and gstin[0:2] == row["customer_gstin"][0:2]:
itm_det.update({"camt": flt(tax/2.0, 2), "samt": flt(tax/2.0, 2)})
else:
itm_det.update({"iamt": tax})
return {"num": int(num), "itm_det": itm_det}
def get_company_gstin_number(company):
filters = [
["is_your_company_address", "=", 1],
["Dynamic Link", "link_doctype", "=", "Company"],
["Dynamic Link", "link_name", "=", company],
["Dynamic Link", "parenttype", "=", "Address"],
]
gstin = frappe.get_all("Address", filters=filters, fields=["gstin"])
if gstin:
return gstin[0]["gstin"]
else:
frappe.throw(_("No GST No. found for the Company."))
def download_json_file(filename, report_type, data):
''' download json content in a file '''
frappe.response['filename'] = frappe.scrub("{0} {1}".format(filename, report_type)) + '.json'
frappe.response['filecontent'] = json.dumps(data)
frappe.response['content_type'] = 'application/json'
frappe.response['type'] = 'download'

View File

@ -97,6 +97,8 @@ class Quotation(SellingController):
self.update_lead() self.update_lead()
def on_cancel(self): def on_cancel(self):
super(Quotation, self).on_cancel()
#update enquiry status #update enquiry status
self.set_status(update=True) self.set_status(update=True)
self.update_opportunity() self.update_opportunity()

View File

@ -183,6 +183,8 @@ class SalesOrder(SellingController):
self.update_blanket_order() self.update_blanket_order()
def on_cancel(self): def on_cancel(self):
super(SalesOrder, self).on_cancel()
# Cannot cancel closed SO # Cannot cancel closed SO
if self.status == 'Closed': if self.status == 'Closed':
frappe.throw(_("Closed order cannot be cancelled. Unclose to cancel.")) frappe.throw(_("Closed order cannot be cancelled. Unclose to cancel."))

View File

@ -2,7 +2,7 @@
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import flt, add_days from frappe.utils import flt, add_days, nowdate
import frappe.permissions import frappe.permissions
import unittest import unittest
from erpnext.selling.doctype.sales_order.sales_order \ from erpnext.selling.doctype.sales_order.sales_order \
@ -13,7 +13,6 @@ from erpnext.controllers.accounts_controller import update_child_qty_rate
import json import json
from erpnext.selling.doctype.sales_order.sales_order import make_raw_material_request from erpnext.selling.doctype.sales_order.sales_order import make_raw_material_request
class TestSalesOrder(unittest.TestCase): class TestSalesOrder(unittest.TestCase):
def tearDown(self): def tearDown(self):
frappe.set_user("Administrator") frappe.set_user("Administrator")
@ -710,6 +709,28 @@ class TestSalesOrder(unittest.TestCase):
se.cancel() se.cancel()
self.assertFalse(frappe.db.exists("Serial No", {"sales_order": so.name})) self.assertFalse(frappe.db.exists("Serial No", {"sales_order": so.name}))
def test_advance_payment_entry_unlink_against_sales_order(self):
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
frappe.db.set_value("Accounts Settings", "Accounts Settings",
"unlink_advance_payment_on_cancelation_of_order", 0)
so = make_sales_order()
pe = get_payment_entry("Sales Order", so.name, bank_account="_Test Bank - _TC")
pe.reference_no = "1"
pe.reference_date = nowdate()
pe.paid_from_account_currency = so.currency
pe.paid_to_account_currency = so.currency
pe.source_exchange_rate = 1
pe.target_exchange_rate = 1
pe.paid_amount = so.grand_total
pe.save(ignore_permissions=True)
pe.submit()
so_doc = frappe.get_doc('Sales Order', so.name)
self.assertRaises(frappe.LinkExistsError, so_doc.cancel)
def test_request_for_raw_materials(self): def test_request_for_raw_materials(self):
from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.item.test_item import make_item
item = make_item("_Test Finished Item", {"is_stock_item": 1, item = make_item("_Test Finished Item", {"is_stock_item": 1,

View File

@ -244,4 +244,4 @@
"track_changes": 1, "track_changes": 1,
"track_seen": 0, "track_seen": 0,
"track_views": 0 "track_views": 0
} }

View File

@ -233,9 +233,21 @@ erpnext.pos.PointOfSale = class PointOfSale {
} else { } else {
this.update_item_in_frm(item, field, value) this.update_item_in_frm(item, field, value)
.then(() => { .then(() => {
// update cart frappe.dom.unfreeze();
this.update_cart_data(item); frappe.run_serially([
this.set_form_action(); () => {
let items = this.frm.doc.items.map(item => item.name);
if (items && items.length > 0 && items.includes(item.name)) {
this.frm.doc.items.forEach(item_row => {
// update cart
this.on_qty_change(item_row);
});
} else {
this.on_qty_change(item);
}
},
() => this.post_qty_change(item)
]);
}); });
} }
return; return;
@ -251,7 +263,28 @@ erpnext.pos.PointOfSale = class PointOfSale {
frappe.flags.hide_serial_batch_dialog = true; frappe.flags.hide_serial_batch_dialog = true;
frappe.run_serially([ frappe.run_serially([
() => this.frm.script_manager.trigger('item_code', item.doctype, item.name), () => {
this.frm.script_manager.trigger('item_code', item.doctype, item.name)
.then(() => {
this.frm.script_manager.trigger('qty', item.doctype, item.name)
.then(() => {
frappe.run_serially([
() => {
let items = this.frm.doc.items.map(i => i.name);
if (items && items.length > 0 && items.includes(item.name)) {
this.frm.doc.items.forEach(item_row => {
// update cart
this.on_qty_change(item_row);
});
} else {
this.on_qty_change(item);
}
},
() => this.post_qty_change(item)
]);
});
});
},
() => { () => {
const show_dialog = item.has_serial_no || item.has_batch_no; const show_dialog = item.has_serial_no || item.has_batch_no;
@ -261,14 +294,25 @@ erpnext.pos.PointOfSale = class PointOfSale {
(item.has_serial_no) || (item.actual_batch_qty != item.actual_qty)) ) { (item.has_serial_no) || (item.actual_batch_qty != item.actual_qty)) ) {
// check has serial no/batch no and update cart // check has serial no/batch no and update cart
this.select_batch_and_serial_no(item); this.select_batch_and_serial_no(item);
} else {
// update cart
this.update_cart_data(item);
} }
} }
]); ]);
} }
on_qty_change(item) {
frappe.run_serially([
() => this.update_cart_data(item),
]);
}
post_qty_change(item) {
this.cart.update_taxes_and_totals();
this.cart.update_grand_total();
this.cart.update_qty_total();
this.cart.scroll_to_item(item.item_code);
this.set_form_action();
}
select_batch_and_serial_no(row) { select_batch_and_serial_no(row) {
frappe.dom.unfreeze(); frappe.dom.unfreeze();
@ -283,7 +327,8 @@ erpnext.pos.PointOfSale = class PointOfSale {
frappe.model.clear_doc(item.doctype, item.name); frappe.model.clear_doc(item.doctype, item.name);
} }
}, },
() => this.update_cart_data(item) () => this.update_cart_data(item),
() => this.post_qty_change(item)
]); ]);
}); });
}) })
@ -300,9 +345,6 @@ erpnext.pos.PointOfSale = class PointOfSale {
update_cart_data(item) { update_cart_data(item) {
this.cart.add_item(item); this.cart.add_item(item);
this.cart.update_taxes_and_totals();
this.cart.update_grand_total();
this.cart.update_qty_total();
frappe.dom.unfreeze(); frappe.dom.unfreeze();
} }
@ -446,16 +488,15 @@ erpnext.pos.PointOfSale = class PointOfSale {
} }
setup_company() { setup_company() {
this.company = frappe.sys_defaults.company;
return new Promise(resolve => { return new Promise(resolve => {
if(!this.company) { if(!frappe.sys_defaults.company) {
frappe.prompt({fieldname:"company", options: "Company", fieldtype:"Link", frappe.prompt({fieldname:"company", options: "Company", fieldtype:"Link",
label: __("Select Company"), reqd: 1}, (data) => { label: __("Select Company"), reqd: 1}, (data) => {
this.company = data.company; this.company = data.company;
resolve(this.company); resolve(this.company);
}, __("Select Company")); }, __("Select Company"));
} else { } else {
resolve(this.company); resolve();
} }
}) })
} }
@ -509,7 +550,9 @@ erpnext.pos.PointOfSale = class PointOfSale {
} }
set_pos_profile_data() { set_pos_profile_data() {
this.frm.doc.company = this.company; if (this.company) {
this.frm.doc.company = this.company;
}
return new Promise(resolve => { return new Promise(resolve => {
return this.frm.call({ return this.frm.call({
@ -953,7 +996,6 @@ class POSCart {
$item.appendTo(this.$cart_items); $item.appendTo(this.$cart_items);
} }
this.highlight_item(item.item_code); this.highlight_item(item.item_code);
this.scroll_to_item(item.item_code);
} }
update_item(item) { update_item(item) {
@ -1206,7 +1248,10 @@ class POSItems {
clearTimeout(this.last_search); clearTimeout(this.last_search);
this.last_search = setTimeout(() => { this.last_search = setTimeout(() => {
const search_term = e.target.value; const search_term = e.target.value;
this.filter_items({ search_term }); const item_group = this.item_group_field ?
this.item_group_field.get_value() : '';
this.filter_items({ search_term:search_term, item_group: item_group});
}, 300); }, 300);
}); });

View File

@ -29,7 +29,7 @@ def get_items(start, page_length, price_list, item_group, search_value="", pos_p
batch_no = data.get("batch_no") if data.get("batch_no") else "" batch_no = data.get("batch_no") if data.get("batch_no") else ""
barcode = data.get("barcode") if data.get("barcode") else "" barcode = data.get("barcode") if data.get("barcode") else ""
item_code, condition = get_conditions(item_code, serial_no, batch_no, barcode) condition = get_conditions(item_code, serial_no, batch_no, barcode)
if pos_profile: if pos_profile:
condition += get_item_group_condition(pos_profile) condition += get_item_group_condition(pos_profile)
@ -86,7 +86,6 @@ def get_items(start, page_length, price_list, item_group, search_value="", pos_p
and {condition} limit {start}, {page_length}""".format and {condition} limit {start}, {page_length}""".format
(start=start,page_length=page_length,lft=lft, rgt=rgt, condition=condition), (start=start,page_length=page_length,lft=lft, rgt=rgt, condition=condition),
{ {
'item_code': item_code,
'price_list': price_list, 'price_list': price_list,
'warehouse': warehouse 'warehouse': warehouse
} , as_dict=1) } , as_dict=1)
@ -133,12 +132,10 @@ def search_serial_or_batch_or_barcode_number(search_value):
def get_conditions(item_code, serial_no, batch_no, barcode): def get_conditions(item_code, serial_no, batch_no, barcode):
if serial_no or batch_no or barcode: if serial_no or batch_no or barcode:
return frappe.db.escape(item_code), "i.name = %(item_code)s" return "i.name = {0}".format(frappe.db.escape(item_code))
condition = """(i.name like %(item_code)s return """(i.name like {item_code}
or i.item_name like %(item_code)s)""" or i.item_name like {item_code})""".format(item_code = frappe.db.escape('%' + item_code + '%'))
return frappe.db.escape('%' + item_code + '%'), condition
def get_item_group_condition(pos_profile): def get_item_group_condition(pos_profile):
cond = "and 1=1" cond = "and 1=1"

View File

@ -0,0 +1,211 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import flt
from erpnext.accounts.utils import get_fiscal_year
from erpnext.accounts.report.financial_statements import get_period_list
from erpnext.accounts.doctype.monthly_distribution.monthly_distribution import get_periodwise_distribution_data
def get_data_column(filters, partner_doctype):
data = []
period_list = get_period_list(filters.fiscal_year, filters.fiscal_year,
filters.period, company=filters.company)
rows = get_data(filters, period_list, partner_doctype)
columns = get_columns(filters, period_list, partner_doctype)
if not rows:
return columns, data
for key, value in rows.items():
value.update({
frappe.scrub(partner_doctype): key[0],
'item_group': key[1]
})
data.append(value)
return columns, data
def get_data(filters, period_list, partner_doctype):
sales_field = frappe.scrub(partner_doctype)
sales_users_data = get_parents_data(filters, partner_doctype)
if not sales_users_data: return
sales_users, item_groups = [], []
for d in sales_users_data:
if d.parent not in sales_users:
sales_users.append(d.parent)
if d.item_group not in item_groups:
item_groups.append(d.item_group)
date_field = ("transaction_date"
if filters.get('doctype') == "Sales Order" else "posting_date")
actual_data = get_actual_data(filters, item_groups, sales_users, date_field, sales_field)
return prepare_data(filters, sales_users_data,
actual_data, date_field, period_list, sales_field)
def get_columns(filters, period_list, partner_doctype):
fieldtype, options = "Currency", "currency"
if filters.get("target_on") == 'Quantity':
fieldtype, options = "Float", ""
columns = [{
"fieldname": frappe.scrub(partner_doctype),
"label": _(partner_doctype),
"fieldtype": "Link",
"options": partner_doctype,
"width": 100
}, {
"fieldname": "item_group",
"label": _("Item Group"),
"fieldtype": "Link",
"options": "Item Group",
"width": 100
}]
for period in period_list:
target_key = 'target_{}'.format(period.key)
variance_key = 'variance_{}'.format(period.key)
columns.extend([{
"fieldname": target_key,
"label": _("Target ({})".format(period.label)),
"fieldtype": fieldtype,
"options": options,
"width": 100
}, {
"fieldname": period.key,
"label": _("Achieved ({})".format(period.label)),
"fieldtype": fieldtype,
"options": options,
"width": 100
}, {
"fieldname": variance_key,
"label": _("Variance ({})".format(period.label)),
"fieldtype": fieldtype,
"options": options,
"width": 100
}])
columns.extend([{
"fieldname": "total_target",
"label": _("Total Target"),
"fieldtype": fieldtype,
"options": options,
"width": 100
}, {
"fieldname": "total_achieved",
"label": _("Total Achieved"),
"fieldtype": fieldtype,
"options": options,
"width": 100
}, {
"fieldname": "total_variance",
"label": _("Total Variance"),
"fieldtype": fieldtype,
"options": options,
"width": 100
}])
return columns
def prepare_data(filters, sales_users_data, actual_data, date_field, period_list, sales_field):
rows = {}
target_qty_amt_field = ("target_qty"
if filters.get("target_on") == 'Quantity' else "target_amount")
qty_or_amount_field = ("stock_qty"
if filters.get("target_on") == 'Quantity' else "base_net_amount")
for d in sales_users_data:
key = (d.parent, d.item_group)
dist_data = get_periodwise_distribution_data(d.distribution_id, period_list, filters.get("period"))
if key not in rows:
rows.setdefault(key,{
'total_target': 0,
'total_achieved': 0,
'total_variance': 0
})
details = rows[key]
for period in period_list:
p_key = period.key
if p_key not in details:
details[p_key] = 0
target_key = 'target_{}'.format(p_key)
variance_key = 'variance_{}'.format(p_key)
details[target_key] = (d.get(target_qty_amt_field) * dist_data.get(p_key)) / 100
details[variance_key] = 0
details["total_target"] += details[target_key]
for r in actual_data:
if (r.get(sales_field) == d.parent and r.item_group == d.item_group and
period.from_date <= r.get(date_field) and r.get(date_field) <= period.to_date):
details[p_key] += r.get(qty_or_amount_field, 0)
details[variance_key] = details.get(target_key) - details.get(p_key)
details["total_achieved"] += details.get(p_key)
details["total_variance"] = details.get("total_target") - details.get("total_achieved")
return rows
def get_actual_data(filters, item_groups, sales_users_or_territory_data, date_field, sales_field):
fiscal_year = get_fiscal_year(fiscal_year=filters.get("fiscal_year"), as_dict=1)
dates = [fiscal_year.year_start_date, fiscal_year.year_end_date]
select_field = "`tab{0}`.{1}".format(filters.get("doctype"), sales_field)
child_table = "`tab{0}`".format(filters.get("doctype") + ' Item')
if sales_field == 'sales_person':
select_field = "`tabSales Team`.sales_person"
child_table = "`tab{0}`, `tabSales Team`".format(filters.get("doctype") + ' Item')
cond = """`tabSales Team`.parent = `tab{0}`.name and
`tabSales Team`.sales_person in ({1}) """.format(filters.get("doctype"),
','.join(['%s'] * len(sales_users_or_territory_data)))
else:
cond = "`tab{0}`.{1} in ({2})".format(filters.get("doctype"), sales_field,
','.join(['%s'] * len(sales_users_or_territory_data)))
return frappe.db.sql(""" SELECT `tab{child_doc}`.item_group,
`tab{child_doc}`.stock_qty, `tab{child_doc}`.base_net_amount,
{select_field}, `tab{parent_doc}`.{date_field}
FROM `tab{parent_doc}`, {child_table}
WHERE
`tab{child_doc}`.parent = `tab{parent_doc}`.name
and `tab{parent_doc}`.docstatus = 1 and {cond}
and `tab{child_doc}`.item_group in ({item_groups})
and `tab{parent_doc}`.{date_field} between %s and %s"""
.format(
cond = cond,
date_field = date_field,
select_field = select_field,
child_table = child_table,
parent_doc = filters.get("doctype"),
child_doc = filters.get("doctype") + ' Item',
item_groups = ','.join(['%s'] * len(item_groups))
), tuple(sales_users_or_territory_data + item_groups + dates), as_dict=1)
def get_parents_data(filters, partner_doctype):
filters_dict = {'parenttype': partner_doctype}
target_qty_amt_field = ("target_qty"
if filters.get("target_on") == 'Quantity' else "target_amount")
if filters.get("fiscal_year"):
filters_dict["fiscal_year"] = filters.get("fiscal_year")
return frappe.get_all('Target Detail',
filters = filters_dict,
fields = ["parent", "item_group", target_qty_amt_field, "fiscal_year", "distribution_id"])

View File

@ -0,0 +1,48 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
/* eslint-disable */
frappe.query_reports["Sales Partner Target Variance based on Item Group"] = {
"filters": [
{
fieldname:"company",
label: __("Company"),
fieldtype: "Link",
options: "Company",
default: frappe.defaults.get_user_default("Company")
},
{
fieldname: "fiscal_year",
label: __("Fiscal Year"),
fieldtype: "Link",
options: "Fiscal Year",
default: frappe.sys_defaults.fiscal_year
},
{
fieldname: "doctype",
label: __("Document Type"),
fieldtype: "Select",
options: "Sales Order\nDelivery Note\nSales Invoice",
default: "Sales Order"
},
{
fieldname: "period",
label: __("Period"),
fieldtype: "Select",
options: [
{ "value": "Monthly", "label": __("Monthly") },
{ "value": "Quarterly", "label": __("Quarterly") },
{ "value": "Half-Yearly", "label": __("Half-Yearly") },
{ "value": "Yearly", "label": __("Yearly") }
],
default: "Monthly"
},
{
fieldname: "target_on",
label: __("Target On"),
fieldtype: "Select",
options: "Quantity\nAmount",
default: "Quantity"
},
]
}

View File

@ -0,0 +1,37 @@
{
"add_total_row": 0,
"creation": "2019-03-25 18:22:37.323995",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"letter_head": "Gadgets International",
"modified": "2019-03-25 18:22:37.323995",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Partner Target Variance based on Item Group",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Sales Order",
"report_name": "Sales Partner Target Variance based on Item Group",
"report_type": "Script Report",
"roles": [
{
"role": "Sales User"
},
{
"role": "Sales Manager"
},
{
"role": "Maintenance User"
},
{
"role": "Accounts User"
},
{
"role": "Stock User"
}
]
}

View File

@ -0,0 +1,12 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from erpnext.selling.report.sales_partner_target_variance_based_on_item_group.item_group_wise_sales_target_variance import get_data_column
def execute(filters=None):
data = []
return get_data_column(filters, "Sales Partner")

View File

@ -2,7 +2,7 @@
// For license information, please see license.txt // For license information, please see license.txt
/* eslint-disable */ /* eslint-disable */
frappe.query_reports["Sales Partner-wise Transaction Summary"] = { frappe.query_reports["Sales Partner Transaction Summary"] = {
"filters": [ "filters": [
{ {
fieldname: "sales_partner", fieldname: "sales_partner",

View File

@ -1,6 +1,6 @@
{ {
"add_total_row": 1, "add_total_row": 1,
"creation": "2019-03-15 16:21:16.088831", "creation": "2019-03-25 18:15:09.920739",
"disable_prepared_report": 0, "disable_prepared_report": 0,
"disabled": 0, "disabled": 0,
"docstatus": 0, "docstatus": 0,
@ -8,14 +8,14 @@
"idx": 0, "idx": 0,
"is_standard": "Yes", "is_standard": "Yes",
"letter_head": "Gadgets International", "letter_head": "Gadgets International",
"modified": "2019-03-15 16:21:16.088831", "modified": "2019-03-25 18:15:09.920739",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Sales Partner-wise Transaction Summary", "name": "Sales Partner Transaction Summary",
"owner": "Administrator", "owner": "Administrator",
"prepared_report": 0, "prepared_report": 0,
"ref_doctype": "Sales Order", "ref_doctype": "Sales Order",
"report_name": "Sales Partner-wise Transaction Summary", "report_name": "Sales Partner Transaction Summary",
"report_type": "Script Report", "report_type": "Script Report",
"roles": [ "roles": [
{ {

View File

@ -1,8 +1,16 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// License: GNU General Public License v3. See license.txt // For license information, please see license.txt
/* eslint-disable */
frappe.query_reports["Territory Target Variance Item Group-Wise"] = { frappe.query_reports["Sales Person Target Variance Based On Item Group"] = {
"filters": [ "filters": [
{
fieldname:"company",
label: __("Company"),
fieldtype: "Link",
options: "Company",
default: frappe.defaults.get_user_default("Company")
},
{ {
fieldname: "fiscal_year", fieldname: "fiscal_year",
label: __("Fiscal Year"), label: __("Fiscal Year"),
@ -10,6 +18,13 @@ frappe.query_reports["Territory Target Variance Item Group-Wise"] = {
options: "Fiscal Year", options: "Fiscal Year",
default: frappe.sys_defaults.fiscal_year default: frappe.sys_defaults.fiscal_year
}, },
{
fieldname: "doctype",
label: __("Document Type"),
fieldtype: "Select",
options: "Sales Order\nDelivery Note\nSales Invoice",
default: "Sales Order"
},
{ {
fieldname: "period", fieldname: "period",
label: __("Period"), label: __("Period"),

View File

@ -1,19 +1,21 @@
{ {
"add_total_row": 0, "add_total_row": 0,
"apply_user_permissions": 1, "creation": "2019-03-25 22:16:49.040998",
"creation": "2013-06-21 12:15:00", "disable_prepared_report": 0,
"disabled": 0, "disabled": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Report", "doctype": "Report",
"idx": 3, "idx": 0,
"is_standard": "Yes", "is_standard": "Yes",
"modified": "2017-02-24 20:13:29.705321", "letter_head": "Gadgets International",
"modified": "2019-03-25 22:16:49.040998",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Territory Target Variance Item Group-Wise", "name": "Sales Person Target Variance Based On Item Group",
"owner": "Administrator", "owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Sales Order", "ref_doctype": "Sales Order",
"report_name": "Territory Target Variance Item Group-Wise", "report_name": "Sales Person Target Variance Based On Item Group",
"report_type": "Script Report", "report_type": "Script Report",
"roles": [ "roles": [
{ {

View File

@ -0,0 +1,11 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from erpnext.selling.report.sales_partner_target_variance_based_on_item_group.item_group_wise_sales_target_variance import get_data_column
def execute(filters=None):
data = []
return get_data_column(filters, "Sales Person")

View File

@ -1,178 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from frappe import _, msgprint
from frappe.utils import flt
from erpnext.accounts.utils import get_fiscal_year
from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ranges
def execute(filters=None):
if not filters: filters = {}
columns = get_columns(filters)
period_month_ranges = get_period_month_ranges(filters["period"], filters["fiscal_year"])
sim_map = get_salesperson_item_month_map(filters)
data = []
for salesperson, salesperson_items in sim_map.items():
for item_group, monthwise_data in salesperson_items.items():
row = [salesperson, item_group]
totals = [0, 0, 0]
for relevant_months in period_month_ranges:
period_data = [0, 0, 0]
for month in relevant_months:
month_data = monthwise_data.get(month, {})
for i, fieldname in enumerate(["target", "achieved", "variance"]):
value = flt(month_data.get(fieldname))
period_data[i] += value
totals[i] += value
period_data[2] = period_data[0] - period_data[1]
row += period_data
totals[2] = totals[0] - totals[1]
row += totals
data.append(row)
return columns, sorted(data, key=lambda x: (x[0], x[1]))
def get_columns(filters):
for fieldname in ["fiscal_year", "period", "target_on"]:
if not filters.get(fieldname):
label = (" ".join(fieldname.split("_"))).title()
msgprint(_("Please specify") + ": " + label,
raise_exception=True)
columns = [_("Sales Person") + ":Link/Sales Person:120", _("Item Group") + ":Link/Item Group:120"]
group_months = False if filters["period"] == "Monthly" else True
for from_date, to_date in get_period_date_ranges(filters["period"], filters["fiscal_year"]):
for label in [_("Target") + " (%s)", _("Achieved") + " (%s)", _("Variance") + " (%s)"]:
if group_months:
label = label % (_(from_date.strftime("%b")) + " - " + _(to_date.strftime("%b")))
else:
label = label % _(from_date.strftime("%b"))
columns.append(label+":Float:120")
return columns + [_("Total Target") + ":Float:120", _("Total Achieved") + ":Float:120",
_("Total Variance") + ":Float:120"]
#Get sales person & item group details
def get_salesperson_details(filters):
return frappe.db.sql("""
select
sp.name, td.item_group, td.target_qty, td.target_amount, sp.distribution_id
from
`tabSales Person` sp, `tabTarget Detail` td
where
td.parent=sp.name and td.fiscal_year=%s order by sp.name
""", (filters["fiscal_year"]), as_dict=1)
#Get target distribution details of item group
def get_target_distribution_details(filters):
target_details = {}
for d in frappe.db.sql("""
select
md.name, mdp.month, mdp.percentage_allocation
from
`tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md
where
mdp.parent=md.name and md.fiscal_year=%s
""", (filters["fiscal_year"]), as_dict=1):
target_details.setdefault(d.name, {}).setdefault(d.month, flt(d.percentage_allocation))
return target_details
#Get achieved details from sales order
def get_achieved_details(filters, sales_person, all_sales_persons, target_item_group, item_groups):
start_date, end_date = get_fiscal_year(fiscal_year = filters["fiscal_year"])[1:]
item_details = frappe.db.sql("""
SELECT st.sales_person, MONTHNAME(so.transaction_date) as month_name,
CASE
WHEN so.status = "Closed" THEN sum(soi.delivered_qty * soi.conversion_factor * (st.allocated_percentage/100))
ELSE sum(soi.stock_qty * (st.allocated_percentage/100))
END as qty,
CASE
WHEN so.status = "Closed" THEN sum(soi.delivered_qty * soi.conversion_factor * soi.base_net_rate * (st.allocated_percentage/100))
ELSE sum(soi.base_net_amount * (st.allocated_percentage/100))
END as amount
from
`tabSales Order Item` soi, `tabSales Order` so, `tabSales Team` st
where
soi.parent=so.name and so.docstatus=1 and st.parent=so.name
and so.transaction_date>=%s and so.transaction_date<=%s
and exists(SELECT name from `tabSales Person` where lft >= %s and rgt <= %s and name=st.sales_person)
and exists(SELECT name from `tabItem Group` where lft >= %s and rgt <= %s and name=soi.item_group)
group by
sales_person, month_name
""",
(start_date, end_date, all_sales_persons[sales_person].lft, all_sales_persons[sales_person].rgt,
item_groups[target_item_group].lft, item_groups[target_item_group].rgt), as_dict=1)
actual_details = {}
for d in item_details:
actual_details.setdefault(d.month_name, frappe._dict({
"quantity" : 0,
"amount" : 0
}))
value_dict = actual_details[d.month_name]
value_dict.quantity += flt(d.qty)
value_dict.amount += flt(d.amount)
return actual_details
def get_salesperson_item_month_map(filters):
import datetime
salesperson_details = get_salesperson_details(filters)
tdd = get_target_distribution_details(filters)
item_groups = get_item_groups()
sales_persons = get_sales_persons()
sales_person_achievement_dict = {}
for sd in salesperson_details:
achieved_details = get_achieved_details(filters, sd.name, sales_persons, sd.item_group, item_groups)
for month_id in range(1, 13):
month = datetime.date(2013, month_id, 1).strftime('%B')
sales_person_achievement_dict.setdefault(sd.name, {}).setdefault(sd.item_group, {})\
.setdefault(month, frappe._dict({
"target": 0.0, "achieved": 0.0
}))
sales_target_achieved = sales_person_achievement_dict[sd.name][sd.item_group][month]
month_percentage = tdd.get(sd.distribution_id, {}).get(month, 0) \
if sd.distribution_id else 100.0/12
if (filters["target_on"] == "Quantity"):
sales_target_achieved.target = flt(sd.target_qty) * month_percentage / 100
else:
sales_target_achieved.target = flt(sd.target_amount) * month_percentage / 100
sales_target_achieved.achieved = achieved_details.get(month, frappe._dict())\
.get(filters["target_on"].lower())
return sales_person_achievement_dict
def get_item_groups():
item_groups = frappe._dict()
for d in frappe.get_all("Item Group", fields=["name", "lft", "rgt"]):
item_groups.setdefault(d.name, frappe._dict({
"lft": d.lft,
"rgt": d.rgt
}))
return item_groups
def get_sales_persons():
sales_persons = frappe._dict()
for d in frappe.get_all("Sales Person", fields=["name", "lft", "rgt"]):
sales_persons.setdefault(d.name, frappe._dict({
"lft": d.lft,
"rgt": d.rgt
}))
return sales_persons

View File

@ -1,8 +1,16 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// License: GNU General Public License v3. See license.txt // For license information, please see license.txt
/* eslint-disable */
frappe.query_reports["Sales Person Target Variance Item Group-Wise"] = { frappe.query_reports["Territory Target Variance Based On Item Group"] = {
"filters": [ "filters": [
{
fieldname:"company",
label: __("Company"),
fieldtype: "Link",
options: "Company",
default: frappe.defaults.get_user_default("Company")
},
{ {
fieldname: "fiscal_year", fieldname: "fiscal_year",
label: __("Fiscal Year"), label: __("Fiscal Year"),
@ -10,6 +18,13 @@ frappe.query_reports["Sales Person Target Variance Item Group-Wise"] = {
options: "Fiscal Year", options: "Fiscal Year",
default: frappe.sys_defaults.fiscal_year default: frappe.sys_defaults.fiscal_year
}, },
{
fieldname: "doctype",
label: __("Document Type"),
fieldtype: "Select",
options: "Sales Order\nDelivery Note\nSales Invoice",
default: "Sales Order"
},
{ {
fieldname: "period", fieldname: "period",
label: __("Period"), label: __("Period"),

View File

@ -1,19 +1,21 @@
{ {
"add_total_row": 0, "add_total_row": 0,
"apply_user_permissions": 1, "creation": "2019-03-25 22:20:59.033199",
"creation": "2013-06-21 12:14:15", "disable_prepared_report": 0,
"disabled": 0, "disabled": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Report", "doctype": "Report",
"idx": 3, "idx": 0,
"is_standard": "Yes", "is_standard": "Yes",
"modified": "2017-02-24 20:14:50.589149", "letter_head": "Gadgets International",
"modified": "2019-03-25 22:20:59.033199",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Sales Person Target Variance Item Group-Wise", "name": "Territory Target Variance Based On Item Group",
"owner": "Administrator", "owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Sales Order", "ref_doctype": "Sales Order",
"report_name": "Sales Person Target Variance Item Group-Wise", "report_name": "Territory Target Variance Based On Item Group",
"report_type": "Script Report", "report_type": "Script Report",
"roles": [ "roles": [
{ {

View File

@ -0,0 +1,11 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from erpnext.selling.report.sales_partner_target_variance_based_on_item_group.item_group_wise_sales_target_variance import get_data_column
def execute(filters=None):
data = []
return get_data_column(filters, "Territory")

View File

@ -1,157 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from frappe import _, msgprint
from frappe.utils import flt
from erpnext.accounts.utils import get_fiscal_year
from erpnext.controllers.trends import get_period_date_ranges, get_period_month_ranges
def execute(filters=None):
if not filters: filters = {}
columns = get_columns(filters)
period_month_ranges = get_period_month_ranges(filters["period"], filters["fiscal_year"])
territory_item_group_dict = get_territory_item_month_map(filters)
data = []
for territory, territory_items in territory_item_group_dict.items():
for item_group, monthwise_data in territory_items.items():
row = [territory, item_group]
totals = [0, 0, 0]
for relevant_months in period_month_ranges:
period_data = [0, 0, 0]
for month in relevant_months:
month_data = monthwise_data.get(month, {})
for i, fieldname in enumerate(["target", "achieved", "variance"]):
value = flt(month_data.get(fieldname))
period_data[i] += value
totals[i] += value
period_data[2] = period_data[0] - period_data[1]
row += period_data
totals[2] = totals[0] - totals[1]
row += totals
data.append(row)
return columns, sorted(data, key=lambda x: (x[0], x[1]))
def get_columns(filters):
for fieldname in ["fiscal_year", "period", "target_on"]:
if not filters.get(fieldname):
label = (" ".join(fieldname.split("_"))).title()
msgprint(_("Please specify") + ": " + label, raise_exception=True)
columns = [_("Territory") + ":Link/Territory:120", _("Item Group") + ":Link/Item Group:120"]
group_months = False if filters["period"] == "Monthly" else True
for from_date, to_date in get_period_date_ranges(filters["period"], filters["fiscal_year"]):
for label in [_("Target") +" (%s)", _("Achieved") + " (%s)", _("Variance") + " (%s)"]:
if group_months:
label = label % (_(from_date.strftime("%b")) + " - " + _(to_date.strftime("%b")))
else:
label = label % _(from_date.strftime("%b"))
columns.append(label+":Float:120")
return columns + [_("Total Target") + ":Float:120", _("Total Achieved") + ":Float:120",
_("Total Variance") + ":Float:120"]
#Get territory & item group details
def get_territory_details(filters):
return frappe.db.sql("""
select
t.name, td.item_group, td.target_qty, td.target_amount, t.distribution_id
from
`tabTerritory` t, `tabTarget Detail` td
where
td.parent=t.name and td.fiscal_year=%s order by t.name
""", (filters["fiscal_year"]), as_dict=1)
#Get target distribution details of item group
def get_target_distribution_details(filters):
target_details = {}
for d in frappe.db.sql("""
select
md.name, mdp.month, mdp.percentage_allocation
from
`tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md
where
mdp.parent=md.name and md.fiscal_year=%s
""", (filters["fiscal_year"]), as_dict=1):
target_details.setdefault(d.name, {}).setdefault(d.month, flt(d.percentage_allocation))
return target_details
#Get achieved details from sales order
def get_achieved_details(filters, territory, item_groups):
start_date, end_date = get_fiscal_year(fiscal_year = filters["fiscal_year"])[1:]
lft, rgt = frappe.db.get_value("Territory", territory, ["lft", "rgt"])
item_details = frappe.db.sql("""
select
soi.item_code, sum(soi.stock_qty) as qty, sum(soi.base_net_amount) as amount,
MONTHNAME(so.transaction_date) as month_name
from
`tabSales Order Item` soi, `tabSales Order` so
where
soi.parent=so.name and so.docstatus=1
and so.transaction_date>=%s and so.transaction_date<=%s
and exists(select name from `tabTerritory` where lft >=%s and rgt <= %s and name=so.territory)
group by
month_name, item_code
""", (start_date, end_date, lft, rgt), as_dict=1)
item_actual_details = {}
for d in item_details:
item_group = item_groups[d.item_code]
item_actual_details.setdefault(item_group, frappe._dict())\
.setdefault(d.month_name, frappe._dict({
"quantity": 0,
"amount": 0
}))
value_dict = item_actual_details[item_group][d.month_name]
value_dict.quantity += flt(d.qty)
value_dict.amount += flt(d.amount)
return item_actual_details
def get_territory_item_month_map(filters):
import datetime
territory_details = get_territory_details(filters)
tdd = get_target_distribution_details(filters)
item_groups = get_item_groups()
territory_item_group_dict = {}
for td in territory_details:
achieved_details = get_achieved_details(filters, td.name, item_groups)
for month_id in range(1, 13):
month = datetime.date(2013, month_id, 1).strftime('%B')
territory_item_group_dict.setdefault(td.name, {}).setdefault(td.item_group, {})\
.setdefault(month, frappe._dict({
"target": 0.0, "achieved": 0.0
}))
target_achieved = territory_item_group_dict[td.name][td.item_group][month]
month_percentage = tdd.get(td.distribution_id, {}).get(month, 0) \
if td.distribution_id else 100.0/12
if (filters["target_on"] == "Quantity"):
target_achieved.target = flt(td.target_qty) * month_percentage / 100
else:
target_achieved.target = flt(td.target_amount) * month_percentage / 100
target_achieved.achieved = achieved_details.get(td.item_group, {}).get(month, {})\
.get(filters["target_on"].lower())
return territory_item_group_dict
def get_item_groups():
return dict(frappe.get_all("Item", fields=["name", "item_group"], as_list=1))

View File

@ -1,6 +1,6 @@
[ [
{ {
"brand": "_Test Brand", "brand": "_Test Brand",
"doctype": "Brand" "doctype": "Brand"
}, },
{ {

View File

@ -22,6 +22,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "details", "fieldname": "details",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
@ -54,6 +55,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "company_name", "fieldname": "company_name",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@ -88,6 +90,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"description": "", "description": "",
"fetch_if_empty": 0,
"fieldname": "abbr", "fieldname": "abbr",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@ -122,6 +125,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:!doc.__islocal && in_list(frappe.user_roles, \"System Manager\")", "depends_on": "eval:!doc.__islocal && in_list(frappe.user_roles, \"System Manager\")",
"fetch_if_empty": 0,
"fieldname": "change_abbr", "fieldname": "change_abbr",
"fieldtype": "Button", "fieldtype": "Button",
"hidden": 0, "hidden": 0,
@ -153,6 +157,7 @@
"bold": 1, "bold": 1,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "is_group", "fieldname": "is_group",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "hidden": 0,
@ -185,6 +190,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "default_finance_book", "fieldname": "default_finance_book",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -218,6 +224,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "cb0", "fieldname": "cb0",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"hidden": 0, "hidden": 0,
@ -248,6 +255,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "domain", "fieldname": "domain",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -280,6 +288,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "parent_company", "fieldname": "parent_company",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -313,6 +322,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "sb_about", "fieldname": "sb_about",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
@ -345,6 +355,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "company_logo", "fieldname": "company_logo",
"fieldtype": "Attach Image", "fieldtype": "Attach Image",
"hidden": 0, "hidden": 0,
@ -377,6 +388,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "company_description", "fieldname": "company_description",
"fieldtype": "Text Editor", "fieldtype": "Text Editor",
"hidden": 0, "hidden": 0,
@ -409,6 +421,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "sales_settings", "fieldname": "sales_settings",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
@ -441,6 +454,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "sales_monthly_history", "fieldname": "sales_monthly_history",
"fieldtype": "Small Text", "fieldtype": "Small Text",
"hidden": 1, "hidden": 1,
@ -473,6 +487,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "transactions_annual_history", "fieldname": "transactions_annual_history",
"fieldtype": "Code", "fieldtype": "Code",
"hidden": 1, "hidden": 1,
@ -505,6 +520,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "monthly_sales_target", "fieldname": "monthly_sales_target",
"fieldtype": "Currency", "fieldtype": "Currency",
"hidden": 0, "hidden": 0,
@ -538,6 +554,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_goals", "fieldname": "column_break_goals",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"hidden": 0, "hidden": 0,
@ -569,6 +586,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "total_monthly_sales", "fieldname": "total_monthly_sales",
"fieldtype": "Currency", "fieldtype": "Currency",
"hidden": 0, "hidden": 0,
@ -602,6 +620,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "charts_section", "fieldname": "charts_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
@ -633,6 +652,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "default_currency", "fieldname": "default_currency",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -665,6 +685,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "default_letter_head", "fieldname": "default_letter_head",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -698,6 +719,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "default_holiday_list", "fieldname": "default_holiday_list",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -731,6 +753,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "standard_working_hours", "fieldname": "standard_working_hours",
"fieldtype": "Float", "fieldtype": "Float",
"hidden": 0, "hidden": 0,
@ -763,6 +786,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "default_terms", "fieldname": "default_terms",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -795,6 +819,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_10", "fieldname": "column_break_10",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"hidden": 0, "hidden": 0,
@ -826,6 +851,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "country", "fieldname": "country",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -858,6 +884,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "create_chart_of_accounts_based_on", "fieldname": "create_chart_of_accounts_based_on",
"fieldtype": "Select", "fieldtype": "Select",
"hidden": 0, "hidden": 0,
@ -892,6 +919,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Standard Template\"", "depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Standard Template\"",
"fetch_if_empty": 0,
"fieldname": "chart_of_accounts", "fieldname": "chart_of_accounts",
"fieldtype": "Select", "fieldtype": "Select",
"hidden": 0, "hidden": 0,
@ -926,6 +954,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Existing Company\"", "depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Existing Company\"",
"fetch_if_empty": 0,
"fieldname": "existing_company", "fieldname": "existing_company",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -959,6 +988,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "tax_id", "fieldname": "tax_id",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@ -991,6 +1021,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "date_of_establishment", "fieldname": "date_of_establishment",
"fieldtype": "Date", "fieldtype": "Date",
"hidden": 0, "hidden": 0,
@ -1023,6 +1054,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "default_settings", "fieldname": "default_settings",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
@ -1056,6 +1088,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:!doc.__islocal", "depends_on": "eval:!doc.__islocal",
"fetch_if_empty": 0,
"fieldname": "default_bank_account", "fieldname": "default_bank_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -1091,6 +1124,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:!doc.__islocal", "depends_on": "eval:!doc.__islocal",
"fetch_if_empty": 0,
"fieldname": "default_cash_account", "fieldname": "default_cash_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -1124,6 +1158,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:!doc.__islocal", "depends_on": "eval:!doc.__islocal",
"fetch_if_empty": 0,
"fieldname": "default_receivable_account", "fieldname": "default_receivable_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -1158,6 +1193,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "round_off_account", "fieldname": "round_off_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -1191,6 +1227,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "round_off_cost_center", "fieldname": "round_off_cost_center",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -1224,6 +1261,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "write_off_account", "fieldname": "write_off_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -1257,6 +1295,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "discount_allowed_account", "fieldname": "discount_allowed_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -1290,6 +1329,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "discount_received_account", "fieldname": "discount_received_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -1323,6 +1363,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "exchange_gain_loss_account", "fieldname": "exchange_gain_loss_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -1356,6 +1397,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "unrealized_exchange_gain_loss_account", "fieldname": "unrealized_exchange_gain_loss_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -1389,6 +1431,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break0", "fieldname": "column_break0",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"hidden": 0, "hidden": 0,
@ -1414,6 +1457,40 @@
"unique": 0, "unique": 0,
"width": "50%" "width": "50%"
}, },
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.parent_company",
"fetch_if_empty": 0,
"fieldname": "allow_account_creation_against_child_company",
"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": "Allow Account Creation Against Child Company",
"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_bulk_edit": 0,
"allow_in_quick_entry": 0, "allow_in_quick_entry": 0,
@ -1422,6 +1499,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:!doc.__islocal", "depends_on": "eval:!doc.__islocal",
"fetch_if_empty": 0,
"fieldname": "default_payable_account", "fieldname": "default_payable_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -1456,6 +1534,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "default_employee_advance_account", "fieldname": "default_employee_advance_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -1490,6 +1569,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:!doc.__islocal", "depends_on": "eval:!doc.__islocal",
"fetch_if_empty": 0,
"fieldname": "default_expense_account", "fieldname": "default_expense_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -1523,6 +1603,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:!doc.__islocal", "depends_on": "eval:!doc.__islocal",
"fetch_if_empty": 0,
"fieldname": "default_income_account", "fieldname": "default_income_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -1556,6 +1637,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:!doc.__islocal", "depends_on": "eval:!doc.__islocal",
"fetch_if_empty": 0,
"fieldname": "default_deferred_revenue_account", "fieldname": "default_deferred_revenue_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -1590,6 +1672,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:!doc.__islocal", "depends_on": "eval:!doc.__islocal",
"fetch_if_empty": 0,
"fieldname": "default_deferred_expense_account", "fieldname": "default_deferred_expense_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -1624,6 +1707,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:!doc.__islocal", "depends_on": "eval:!doc.__islocal",
"fetch_if_empty": 0,
"fieldname": "default_payroll_payable_account", "fieldname": "default_payroll_payable_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -1658,6 +1742,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:!doc.__islocal", "depends_on": "eval:!doc.__islocal",
"fetch_if_empty": 0,
"fieldname": "default_expense_claim_payable_account", "fieldname": "default_expense_claim_payable_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -1691,6 +1776,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "section_break_22", "fieldname": "section_break_22",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
@ -1723,6 +1809,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:!doc.__islocal", "depends_on": "eval:!doc.__islocal",
"fetch_if_empty": 0,
"fieldname": "cost_center", "fieldname": "cost_center",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -1755,6 +1842,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_26", "fieldname": "column_break_26",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"hidden": 0, "hidden": 0,
@ -1787,6 +1875,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:!doc.__islocal", "depends_on": "eval:!doc.__islocal",
"fetch_if_empty": 0,
"fieldname": "credit_limit", "fieldname": "credit_limit",
"fieldtype": "Currency", "fieldtype": "Currency",
"hidden": 0, "hidden": 0,
@ -1822,6 +1911,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "", "depends_on": "",
"fetch_if_empty": 0,
"fieldname": "payment_terms", "fieldname": "payment_terms",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -1856,6 +1946,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:!doc.__islocal", "depends_on": "eval:!doc.__islocal",
"fetch_if_empty": 0,
"fieldname": "auto_accounting_for_stock_settings", "fieldname": "auto_accounting_for_stock_settings",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
@ -1888,6 +1979,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"default": "1", "default": "1",
"fetch_if_empty": 0,
"fieldname": "enable_perpetual_inventory", "fieldname": "enable_perpetual_inventory",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "hidden": 0,
@ -1920,6 +2012,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "default_inventory_account", "fieldname": "default_inventory_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -1953,6 +2046,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "stock_adjustment_account", "fieldname": "stock_adjustment_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -1985,6 +2079,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_32", "fieldname": "column_break_32",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"hidden": 0, "hidden": 0,
@ -2016,6 +2111,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "stock_received_but_not_billed", "fieldname": "stock_received_but_not_billed",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -2048,6 +2144,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "expenses_included_in_valuation", "fieldname": "expenses_included_in_valuation",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -2080,6 +2177,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "fixed_asset_depreciation_settings", "fieldname": "fixed_asset_depreciation_settings",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
@ -2112,6 +2210,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "accumulated_depreciation_account", "fieldname": "accumulated_depreciation_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -2145,6 +2244,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "depreciation_expense_account", "fieldname": "depreciation_expense_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -2178,6 +2278,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "series_for_depreciation_entry", "fieldname": "series_for_depreciation_entry",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@ -2210,6 +2311,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "expenses_included_in_asset_valuation", "fieldname": "expenses_included_in_asset_valuation",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -2243,6 +2345,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_40", "fieldname": "column_break_40",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"hidden": 0, "hidden": 0,
@ -2274,6 +2377,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "disposal_account", "fieldname": "disposal_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -2307,6 +2411,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "depreciation_cost_center", "fieldname": "depreciation_cost_center",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -2340,6 +2445,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "capital_work_in_progress_account", "fieldname": "capital_work_in_progress_account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -2373,6 +2479,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "asset_received_but_not_billed", "fieldname": "asset_received_but_not_billed",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -2406,6 +2513,7 @@
"bold": 0, "bold": 0,
"collapsible": 1, "collapsible": 1,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "budget_detail", "fieldname": "budget_detail",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
@ -2438,6 +2546,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "exception_budget_approver_role", "fieldname": "exception_budget_approver_role",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -2472,6 +2581,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"description": "For reference only.", "description": "For reference only.",
"fetch_if_empty": 0,
"fieldname": "company_info", "fieldname": "company_info",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
@ -2503,6 +2613,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "date_of_incorporation", "fieldname": "date_of_incorporation",
"fieldtype": "Date", "fieldtype": "Date",
"hidden": 0, "hidden": 0,
@ -2535,6 +2646,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "address_html", "fieldname": "address_html",
"fieldtype": "HTML", "fieldtype": "HTML",
"hidden": 0, "hidden": 0,
@ -2566,6 +2678,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break1", "fieldname": "column_break1",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"hidden": 0, "hidden": 0,
@ -2599,6 +2712,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:doc.date_of_incorporation", "depends_on": "eval:doc.date_of_incorporation",
"fetch_if_empty": 0,
"fieldname": "date_of_commencement", "fieldname": "date_of_commencement",
"fieldtype": "Date", "fieldtype": "Date",
"hidden": 0, "hidden": 0,
@ -2631,6 +2745,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "phone_no", "fieldname": "phone_no",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@ -2665,6 +2780,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "fax", "fieldname": "fax",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@ -2699,6 +2815,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "email", "fieldname": "email",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@ -2733,6 +2850,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "website", "fieldname": "website",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@ -2767,6 +2885,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"description": "", "description": "",
"fetch_if_empty": 0,
"fieldname": "registration_info", "fieldname": "registration_info",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
@ -2801,6 +2920,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"description": "Company registration numbers for your reference. Tax numbers etc.", "description": "Company registration numbers for your reference. Tax numbers etc.",
"fetch_if_empty": 0,
"fieldname": "registration_details", "fieldname": "registration_details",
"fieldtype": "Code", "fieldtype": "Code",
"hidden": 0, "hidden": 0,
@ -2834,6 +2954,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "delete_company_transactions", "fieldname": "delete_company_transactions",
"fieldtype": "Button", "fieldtype": "Button",
"hidden": 0, "hidden": 0,
@ -2866,6 +2987,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "lft", "fieldname": "lft",
"fieldtype": "Int", "fieldtype": "Int",
"hidden": 1, "hidden": 1,
@ -2898,6 +3020,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "rgt", "fieldname": "rgt",
"fieldtype": "Int", "fieldtype": "Int",
"hidden": 1, "hidden": 1,
@ -2930,6 +3053,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "old_parent", "fieldname": "old_parent",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 1, "hidden": 1,
@ -2969,7 +3093,7 @@
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"menu_index": 0, "menu_index": 0,
"modified": "2019-01-15 13:29:54.510379", "modified": "2019-03-26 17:15:50.390548",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Setup", "module": "Setup",
"name": "Company", "name": "Company",

View File

@ -13,5 +13,16 @@ frappe.ui.form.on('Sales Partner', {
unhide_field(['address_html', 'contact_html', 'address_contacts']); unhide_field(['address_html', 'contact_html', 'address_contacts']);
frappe.contacts.render_address_and_contact(frm); frappe.contacts.render_address_and_contact(frm);
} }
},
setup: function(frm) {
frm.fields_dict["targets"].grid.get_field("distribution_id").get_query = function(doc, cdt, cdn){
var row = locals[cdt][cdn];
return {
filters: {
'fiscal_year': row.fiscal_year
}
}
};
} }
}); });

View File

@ -1,5 +1,6 @@
{ {
"allow_copy": 0, "allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0, "allow_guest_to_view": 0,
"allow_import": 1, "allow_import": 1,
"allow_rename": 1, "allow_rename": 1,
@ -21,6 +22,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "partner_name", "fieldname": "partner_name",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@ -45,7 +47,7 @@
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 1
}, },
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
@ -54,6 +56,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "partner_type", "fieldname": "partner_type",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -89,6 +92,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"description": "", "description": "",
"fetch_if_empty": 0,
"fieldname": "territory", "fieldname": "territory",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -121,6 +125,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break0", "fieldname": "column_break0",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"hidden": 0, "hidden": 0,
@ -153,6 +158,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "commission_rate", "fieldname": "commission_rate",
"fieldtype": "Float", "fieldtype": "Float",
"hidden": 0, "hidden": 0,
@ -186,6 +192,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "address_contacts", "fieldname": "address_contacts",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
@ -218,6 +225,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:doc.__islocal", "depends_on": "eval:doc.__islocal",
"fetch_if_empty": 0,
"fieldname": "address_desc", "fieldname": "address_desc",
"fieldtype": "HTML", "fieldtype": "HTML",
"hidden": 0, "hidden": 0,
@ -249,6 +257,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "address_html", "fieldname": "address_html",
"fieldtype": "HTML", "fieldtype": "HTML",
"hidden": 0, "hidden": 0,
@ -280,6 +289,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break1", "fieldname": "column_break1",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"hidden": 0, "hidden": 0,
@ -311,6 +321,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:doc.__islocal", "depends_on": "eval:doc.__islocal",
"fetch_if_empty": 0,
"fieldname": "contact_desc", "fieldname": "contact_desc",
"fieldtype": "HTML", "fieldtype": "HTML",
"hidden": 0, "hidden": 0,
@ -342,6 +353,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "contact_html", "fieldname": "contact_html",
"fieldtype": "HTML", "fieldtype": "HTML",
"hidden": 0, "hidden": 0,
@ -373,6 +385,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "partner_target_details_section_break", "fieldname": "partner_target_details_section_break",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
@ -405,6 +418,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "targets", "fieldname": "targets",
"fieldtype": "Table", "fieldtype": "Table",
"hidden": 0, "hidden": 0,
@ -439,41 +453,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"description": "Select Monthly Distribution to unevenly distribute targets across months.", "fetch_if_empty": 0,
"fieldname": "distribution_id",
"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": "Target Distribution",
"length": 0,
"no_copy": 0,
"oldfieldname": "distribution_id",
"oldfieldtype": "Link",
"options": "Monthly Distribution",
"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": 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": "website", "fieldname": "website",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
@ -505,6 +485,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "show_in_website", "fieldname": "show_in_website",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "hidden": 0,
@ -537,6 +518,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "show_in_website", "depends_on": "show_in_website",
"fetch_if_empty": 0,
"fieldname": "section_break_17", "fieldname": "section_break_17",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
@ -567,6 +549,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "route", "fieldname": "route",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@ -599,6 +582,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_20", "fieldname": "column_break_20",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"hidden": 0, "hidden": 0,
@ -629,6 +613,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "logo", "fieldname": "logo",
"fieldtype": "Attach", "fieldtype": "Attach",
"hidden": 0, "hidden": 0,
@ -661,6 +646,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "partner_website", "fieldname": "partner_website",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@ -693,6 +679,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "show_in_website", "depends_on": "show_in_website",
"fetch_if_empty": 0,
"fieldname": "section_break_22", "fieldname": "section_break_22",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
@ -723,6 +710,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "introduction", "fieldname": "introduction",
"fieldtype": "Text", "fieldtype": "Text",
"hidden": 0, "hidden": 0,
@ -754,6 +742,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "description", "fieldname": "description",
"fieldtype": "Text Editor", "fieldtype": "Text Editor",
"hidden": 0, "hidden": 0,
@ -790,7 +779,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2018-06-11 13:47:04.182339", "modified": "2019-03-21 16:26:45.447265",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Setup", "module": "Setup",
"name": "Sales Partner", "name": "Sales Partner",
@ -860,5 +849,6 @@
"show_name_in_global_search": 1, "show_name_in_global_search": 1,
"sort_order": "ASC", "sort_order": "ASC",
"track_changes": 0, "track_changes": 0,
"track_seen": 0 "track_seen": 0,
"track_views": 0
} }

View File

@ -8,6 +8,17 @@ frappe.ui.form.on('Sales Person', {
frm.dashboard.add_indicator(__('Total Contribution Amount: {0}', frm.dashboard.add_indicator(__('Total Contribution Amount: {0}',
[format_currency(info.allocated_amount, info.currency)]), 'blue'); [format_currency(info.allocated_amount, info.currency)]), 'blue');
} }
},
setup: function(frm) {
frm.fields_dict["targets"].grid.get_field("distribution_id").get_query = function(doc, cdt, cdn){
var row = locals[cdt][cdn];
return {
filters: {
'fiscal_year': row.fiscal_year
}
}
};
} }
}); });

View File

@ -22,6 +22,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "name_and_employee_id", "fieldname": "name_and_employee_id",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
@ -54,6 +55,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "sales_person_name", "fieldname": "sales_person_name",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@ -88,6 +90,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"description": "Select company name first.", "description": "Select company name first.",
"fetch_if_empty": 0,
"fieldname": "parent_sales_person", "fieldname": "parent_sales_person",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -122,6 +125,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "commission_rate", "fieldname": "commission_rate",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@ -154,6 +158,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "is_group", "fieldname": "is_group",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "hidden": 0,
@ -189,6 +194,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"default": "1", "default": "1",
"fetch_if_empty": 0,
"fieldname": "enabled", "fieldname": "enabled",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "hidden": 0,
@ -221,6 +227,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "cb0", "fieldname": "cb0",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"hidden": 0, "hidden": 0,
@ -251,6 +258,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "employee", "fieldname": "employee",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -284,6 +292,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_from": "employee.department", "fetch_from": "employee.department",
"fetch_if_empty": 0,
"fieldname": "department", "fieldname": "department",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -317,6 +326,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "lft", "fieldname": "lft",
"fieldtype": "Int", "fieldtype": "Int",
"hidden": 1, "hidden": 1,
@ -350,6 +360,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "rgt", "fieldname": "rgt",
"fieldtype": "Int", "fieldtype": "Int",
"hidden": 1, "hidden": 1,
@ -383,6 +394,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "old_parent", "fieldname": "old_parent",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 1, "hidden": 1,
@ -417,6 +429,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"description": "Set targets Item Group-wise for this Sales Person.", "description": "Set targets Item Group-wise for this Sales Person.",
"fetch_if_empty": 0,
"fieldname": "target_details_section_break", "fieldname": "target_details_section_break",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
@ -450,6 +463,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "targets", "fieldname": "targets",
"fieldtype": "Table", "fieldtype": "Table",
"hidden": 0, "hidden": 0,
@ -476,41 +490,6 @@
"set_only_once": 0, "set_only_once": 0,
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Select Monthly Distribution to unevenly distribute targets across months.",
"fieldname": "distribution_id",
"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": "Target Distribution",
"length": 0,
"no_copy": 0,
"oldfieldname": "distribution_id",
"oldfieldtype": "Link",
"options": "Monthly Distribution",
"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": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
} }
], ],
"has_web_view": 0, "has_web_view": 0,
@ -524,7 +503,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2019-01-30 11:28:16.966735", "modified": "2019-03-21 16:26:01.706129",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Setup", "module": "Setup",
"name": "Sales Person", "name": "Sales Person",

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