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 frappe.utils import getdate
__version__ = '11.1.16'
__version__ = '11.1.17'
def get_default_company(user=None):
'''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
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.general_ledger import make_gl_entries
from frappe.email import sendmail_to_system_managers
def validate_service_stop_date(doc):
''' 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)))
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
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
''', (end_date or today(), start_date or add_months(today(), -1)))
''', (end_date, start_date))
# For each invoice, book deferred expense
for invoice in invoices:
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):
# 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
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
''', (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:
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"
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('''
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
''', (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:
booking_start_date = item.service_start_date
elif prev_gl_entry:
booking_start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1))
skip = True if booking_start_date > booking_end_date else False
if prev_gl_entry:
start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1))
else:
start_date = item.service_start_date
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):
account_currency = get_account_currency(item.expense_account)
if end_date > getdate(posting_date):
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":
total_credit_debit, total_credit_debit_currency = "debit", "debit_in_account_currency"
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
def book_deferred_income_or_expense(doc, 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
# start_date: 1st of the last month or the start date
# end_date: end_date or today-1
def book_deferred_income_or_expense(doc, posting_date=None):
enable_check = "enable_deferred_revenue" \
if doc.doctype=="Sales Invoice" else "enable_deferred_expense"
gl_entries = []
for item in doc.get('items'):
if not item.get(enable_check): continue
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
def _book_deferred_revenue_or_expense(item):
start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date)
if not (start_date and end_date): return
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":
against, project = doc.customer, doc.project
credit_account, debit_account = item.income_account, item.deferred_revenue_account
@ -152,16 +152,38 @@ def book_deferred_income_or_expense(doc, start_date=None, end_date=None):
against, project = doc.supplier, item.project
credit_account, debit_account = item.deferred_expense_account, item.expense_account
total_days = date_diff(item.service_end_date, item.service_start_date) + 1
total_booking_days = date_diff(end_date, start_date) + 1
amount, base_amount = calculate_amount(doc, item, last_gl_entry,
total_days, total_booking_days, account_currency)
make_gl_entries(doc, credit_account, debit_account, against,
amount, base_amount, end_date, project, account_currency, item.cost_center, item.name)
if getdate(end_date) < getdate(posting_date) and not last_gl_entry:
_book_deferred_revenue_or_expense(item)
for item in doc.get('items'):
if item.get(enable_check):
_book_deferred_revenue_or_expense(item)
def make_gl_entries(doc, credit_account, debit_account, against,
amount, base_amount, posting_date, project, account_currency, cost_center, voucher_detail_no):
# GL Entry for crediting the amount in the deferred expense
from erpnext.accounts.general_ledger import make_gl_entries
gl_entries = []
gl_entries.append(
doc.get_gl_dict({
"account": credit_account,
"against": against,
"credit": base_amount,
"credit_in_account_currency": amount,
"cost_center": item.cost_center,
"voucher_detail_no": item.name,
'posting_date': booking_end_date,
"cost_center": cost_center,
"voucher_detail_no": voucher_detail_no,
'posting_date': posting_date,
'project': project
}, account_currency)
)
@ -172,16 +194,20 @@ def book_deferred_income_or_expense(doc, start_date=None, end_date=None):
"against": against,
"debit": base_amount,
"debit_in_account_currency": amount,
"cost_center": item.cost_center,
"voucher_detail_no": item.name,
'posting_date': booking_end_date,
"cost_center": cost_center,
"voucher_detail_no": voucher_detail_no,
'posting_date': posting_date,
'project': project
}, account_currency)
)
if gl_entries:
try:
make_gl_entries(gl_entries, cancel=(doc.docstatus == 2), merge_entries=True)
frappe.db.commit()
except:
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)
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]))
else:
descendants = get_descendants_of('Company', self.company)
@ -110,6 +112,8 @@ class Account(NestedSet):
["company", "name"], as_dict=True):
acc_name_map[d["company"]] = d["name"]
if not acc_name_map: return
for company in descendants:
doc = frappe.copy_doc(self)
doc.flags.ignore_root_company_validation = True

View File

@ -23,6 +23,10 @@ frappe.treeview_settings["Account"] = {
if(r.message) {
let root_company = r.message.length ? r.message[0] : "";
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"),
condition: function(node) {
return frappe.boot.user.can_create.indexOf("Account") !== -1 &&
!frappe.treeview_settings['Account'].treeview.page.fields_dict.root_company.get_value() &&
node.expandable && !node.hide_add;
return frappe.boot.user.can_create.indexOf("Account") !== -1
&& (!frappe.treeview_settings['Account'].treeview.page.fields_dict.root_company.get_value()
|| frappe.flags.ignore_root_company_validation)
&& node.expandable && !node.hide_add;
},
click: function() {
var me = frappe.treeview_settings['Account'].treeview;

View File

@ -183,6 +183,10 @@ def get_inventory_account(company, warehouse=None):
return account
def create_account(**kwargs):
account = frappe.db.get_value("Account", filters={"account_name": kwargs.get("account_name"), "company": kwargs.get("company")})
if account:
return account
else:
account = frappe.get_doc(dict(
doctype = "Account",
account_name = kwargs.get('account_name'),

View File

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

View File

@ -42,8 +42,8 @@ class BankReconciliation(Document):
select
"Payment Entry" as payment_document, name as payment_entry,
reference_no as cheque_number, reference_date as cheque_date,
if(paid_from=%(account)s, paid_amount, "") as credit,
if(paid_from=%(account)s, "", received_amount) as debit,
if(paid_from=%(account)s, paid_amount, 0) as credit,
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,
if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency
from `tabPayment Entry`
@ -79,8 +79,12 @@ class BankReconciliation(Document):
for d in 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("debit")
d.pop("account_currency")

View File

@ -52,11 +52,6 @@ class JournalEntry(AccountsController):
self.update_loan()
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):
return self.pay_to_recd_from or self.accounts[0].account

View File

@ -3,8 +3,8 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import flt
from frappe import _
from frappe.utils import (flt, add_months)
from frappe.model.document import Document
class MonthlyDistribution(Document):
@ -25,3 +25,33 @@ class MonthlyDistribution(Document):
if flt(total, 2) != 100.0:
frappe.throw(_("Percentage Allocation should be equal to 100%") + \
" ({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_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):
self.setup_party_account_field()
@ -754,7 +749,7 @@ def get_outstanding_on_journal_entry(name):
@frappe.whitelist()
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)
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"):
outstanding_amount = ref_doc.get("outstanding_amount")
bill_no = ref_doc.get("bill_no")
elif reference_doctype == "Expense Claim":
outstanding_amount = flt(ref_doc.get("total_sanctioned_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"),
"total_amount": total_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):
modified = now()
user = frappe.session.user
company = frappe.db.escape(company)
if pos_profile and company:
frappe.db.sql(""" update `tabPOS Profile User` pfu, `tabPOS Profile` pf

View File

@ -119,7 +119,7 @@ class PricingRule(Document):
#--------------------------------------------------------------------------------
@frappe.whitelist()
def apply_pricing_rule(args):
def apply_pricing_rule(args, doc=None):
"""
args = {
"items": [{"doctype": "", "name": "", "item_code": "", "brand": "", "item_group": ""}, ...],
@ -139,6 +139,7 @@ def apply_pricing_rule(args):
"ignore_pricing_rule": "something"
}
"""
if isinstance(args, string_types):
args = json.loads(args)
@ -161,10 +162,11 @@ def apply_pricing_rule(args):
for item in item_list:
args_copy = copy.deepcopy(args)
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)
if set_serial_nos_based_on_fifo and not args.get('is_return'):
out.append(get_serial_no_for_item(args_copy))
if not item.get("serial_no") and set_serial_nos_based_on_fifo and not args.get('is_return'):
out[0].update(get_serial_no_for_item(args_copy))
return out
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):
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
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:
rules = []
item_details.discount_percentage = 0
item_details.discount_amount = 0
for pricing_rule in pricing_rules:
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))
if pricing_rule.mixed_conditions or pricing_rule.apply_rule_on_other:
continue
@ -243,8 +251,8 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None):
item_details.has_pricing_rule = 1
# if discount is applied on the rate and not on price list rate
if price_list_rate:
set_discount_amount(price_list_rate, item_details)
# if price_list_rate:
# set_discount_amount(price_list_rate, item_details)
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,
'rate_or_discount': pricing_rule.rate_or_discount,
'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')
})
@ -296,6 +304,9 @@ def apply_price_discount_pricing_rule(pricing_rule, item_details, args):
discount_field = "{0}_on_rate".format(field)
item_details[discount_field].append(pricing_rule.get(field, 0))
else:
if field not in item_details:
item_details.setdefault(field, 0)
item_details[field] += (pricing_rule.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
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(','):
if not d: continue
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')
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 = ''
return item_details
@ -368,35 +385,6 @@ def make_pricing_rule(doctype, docname):
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):
items = [filters.get('value')]
if filters.get('apply_on') != 'Item Code':

View File

@ -4,16 +4,21 @@
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
import json
import copy
import frappe, copy, json
from frappe import throw, _
from six import string_types
from frappe.utils import flt, cint, get_datetime
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
from erpnext.stock.get_item_details import get_conversion_factor
class MultiplePricingRuleConflict(frappe.ValidationError): pass
apply_on_table = {
'Item Code': 'items',
'Item Group': 'item_groups',
'Brand': 'brands'
}
def get_pricing_rules(args, doc=None):
pricing_rules = []
values = {}
@ -33,7 +38,9 @@ def get_pricing_rules(args, doc=None):
if pricing_rule:
rules.append(pricing_rule)
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
@ -169,7 +176,7 @@ def filter_pricing_rules(args, pricing_rules, doc=None):
pr_doc = frappe.get_doc('Pricing Rule', pricing_rules[0].name)
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:
items = [args.get(frappe.scrub(pr_doc.get('apply_on')))]
@ -308,7 +315,7 @@ def apply_internal_priority(pricing_rules, field_set, args):
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]
items = get_pricing_rule_items(pr_doc) or []
apply_on = frappe.scrub(pr_doc.get('apply_on'))
@ -318,8 +325,12 @@ def get_qty_and_rate_for_mixed_conditions(doc, pr_doc):
if row.get(apply_on) not in items: continue
if pr_doc.mixed_conditions:
sum_qty += row.stock_qty
sum_amt += row.amount
amt = args.get('qty') * args.get("price_list_rate")
if args.get("item_code") != row.get("item_code"):
amt = row.get('qty') * row.get("price_list_rate")
sum_qty += row.get("stock_qty") or args.get("stock_qty")
sum_amt += amt
if pr_doc.is_cumulative:
data = get_qty_amount_data_for_cumulative(pr_doc, doc, items)
@ -334,8 +345,8 @@ def get_qty_and_rate_for_other_item(doc, pr_doc, pricing_rules):
for d in get_pricing_rule_items(pr_doc):
for row in doc.items:
if d == row.get(frappe.scrub(pr_doc.apply_on)):
pricing_rules = filter_pricing_rules_for_qty_amount(row.stock_qty,
row.amount, pricing_rules, row)
pricing_rules = filter_pricing_rules_for_qty_amount(row.get("stock_qty"),
row.get("amount"), pricing_rules, row)
if pricing_rules and pricing_rules[0]:
return pricing_rules
@ -389,17 +400,15 @@ def get_qty_amount_data_for_cumulative(pr_doc, doc, items=[]):
def validate_pricing_rules(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)
doc.calculate_taxes_and_totals()
def validate_pricing_rule_on_items(doc, item_row):
def validate_pricing_rule_on_items(doc, item_row, do_not_validate = False):
value = 0
for pr_row in get_applied_pricing_rules(doc, item_row):
pr_doc = frappe.get_doc('Pricing Rule', pr_row.pricing_rule)
for pricing_rule in get_applied_pricing_rules(doc, item_row):
pr_doc = frappe.get_doc('Pricing Rule', pricing_rule)
if pr_doc.get('apply_on') == 'Transaction': continue
@ -410,7 +419,7 @@ def validate_pricing_rule_on_items(doc, item_row):
if not pr_doc.get(field): continue
value += pr_doc.get(field)
apply_pricing_rule(doc, pr_doc, pr_row, item_row, value)
apply_pricing_rule(doc, pr_doc, item_row, value, do_not_validate)
def validate_pricing_rule_on_transactions(doc):
conditions = "apply_on = 'Transaction'"
@ -445,8 +454,8 @@ def validate_pricing_rule_on_transactions(doc):
apply_pricing_rule_for_free_items(doc, d)
def get_applied_pricing_rules(doc, item_row):
return [d for d in doc.pricing_rules
if d.child_docname == item_row.name]
return (item_row.get("pricing_rules").split(',')
if item_row.get("pricing_rules") else [])
def apply_pricing_rule_for_free_items(doc, pricing_rule):
if pricing_rule.get('free_item'):
@ -465,7 +474,37 @@ def apply_pricing_rule_for_free_items(doc, pricing_rule):
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, items = get_apply_on_and_items(pr_doc, item_row)
rule_applied = {}
for item in doc.get("items"):
if not item.pricing_rules:
item.pricing_rules = item_row.pricing_rules
if item.get(apply_on) in items:
for field in ['discount_percentage', 'discount_amount', 'rate']:
if not pr_doc.get(field): continue
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)])
@ -474,20 +513,22 @@ def apply_pricing_rule(doc, pr_doc, pr_row, item_row, value):
apply_on = frappe.scrub(pr_doc.apply_rule_on_other)
items = [pr_doc.get(apply_on)]
rule_applied = 1
if item_row.get(apply_on) in items:
for field in ['discount_percentage', 'discount_amount', 'rate']:
if not pr_doc.get(field): continue
if not pr_doc.validate_applied_rule:
item_row.set(field, value)
elif item_row.get(field) < value:
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
return apply_on, items
def get_pricing_rule_items(pr_doc):
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){
var d = locals[cdt][cdn];
if(d.idx == 1 && d.cost_center){
if(d.cost_center){
var cl = doc.items || [];
for(var i = 0; i < cl.length; i++){
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:
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):
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()
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_billing_status_for_zero_amount_refdoc("Purchase Order")
self.update_billing_status_in_pr()

View File

@ -89,6 +89,12 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
}, __('Create'));
}
if (doc.docstatus === 1) {
cur_frm.add_custom_button(__('Maintenance Schedule'), function () {
cur_frm.cscript.make_maintenance_schedule();
}, __('Create'));
}
if(!doc.auto_repeat) {
cur_frm.add_custom_button(__('Subscription'), function() {
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) {
var me = this;

View File

@ -205,18 +205,11 @@ class SalesInvoice(SellingController):
def before_cancel(self):
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):
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
if frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'):
unlink_ref_doc_from_payment_entries(self)
self.check_sales_order_on_hold_or_close("sales_order")
if self.is_return and not self.update_billed_amount_in_sales_order:
# NOTE status updating bypassed for is_return
@ -1230,6 +1223,22 @@ def get_bank_cash_account(mode_of_payment, company):
"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()
def make_delivery_note(source_name, target_doc=None):
def set_missing_values(source, target):

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.stock.doctype.serial_no.serial_no import SerialNoWarehouseError
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.stock.doctype.item.test_item import create_item
from six import iteritems
class TestSalesInvoice(unittest.TestCase):
def make(self):
@ -1572,6 +1573,56 @@ class TestSalesInvoice(unittest.TestCase):
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
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):
si = frappe.new_doc("Sales Invoice")

View File

@ -333,6 +333,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
var me = this;
this.frm = {}
this.load_data(true);
this.frm.doc.offline_pos_name = '';
this.setup();
this.set_default_customer()
},
@ -345,7 +346,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
if (load_doc) {
this.frm.doc = JSON.parse(localStorage.getItem('doc'));
this.frm.doc.offline_pos_name = null;
}
$.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.pos_bill.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();
})
@ -984,7 +984,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
}
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 () {
var me = this;
var existing_pos_list = [];
var invoice_data = {};
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()
//to retrieve and set the default payment
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.outstanding_amount = 0
} else {
this.frm.doc.offline_pos_name = $.now();
} else if(!this.frm.doc.offline_pos_name) {
this.frm.doc.offline_pos_name = frappe.datetime.now_datetime();
this.frm.doc.posting_date = frappe.datetime.get_today();
this.frm.doc.posting_time = frappe.datetime.now_time();
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:
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
FROM `tabGL Entry`
WHERE
party_type = %s and against_voucher is null
GROUP BY party"""
.format(("credit") if party_type == "Customer" else "debit") , party_type)
and {1} GROUP BY party"""
.format(("credit") if party_type == "Customer" else "debit", cond) , party_type)
if 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_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):
row = [party]
@ -144,6 +145,9 @@ class AccountsReceivableSummary(ReceivablePayableReport):
row += [self.get_party_name(args.get("party_type"), party)]
row += [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 += [

View File

@ -14,7 +14,7 @@ def execute(filters=None):
def get_column():
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",
_("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120",
_("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Pending Amount") + ":Currency:100",

View File

@ -17,7 +17,7 @@ def get_ordered_to_be_billed_data(args):
return frappe.db.sql("""
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,
(`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1)),
(`{child_tab}`.base_amount - (`{child_tab}`.billed_amt * ifnull(`{parent_tab}`.conversion_rate, 1))),

View File

@ -14,7 +14,7 @@ def execute(filters=None):
def get_column():
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",
_("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120",
_("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Pending Amount") + ":Currency:100",

View File

@ -133,6 +133,13 @@ def get_columns(filters):
"options": filters.get("based_on"),
"width": 300
},
{
"fieldname": "currency",
"label": _("Currency"),
"fieldtype": "Link",
"options": "Currency",
"hidden": 1
},
{
"fieldname": "income",
"label": _("Income"),
@ -153,13 +160,6 @@ def get_columns(filters):
"fieldtype": "Currency",
"options": "currency",
"width": 120
},
{
"fieldname": "currency",
"label": _("Currency"),
"fieldtype": "Link",
"options": "Currency",
"hidden": 1
}
]

View File

@ -14,7 +14,7 @@ def execute(filters=None):
def get_column():
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",
_("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120",
_("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Amount to Bill") + ":Currency:100",

View File

@ -14,7 +14,7 @@ def execute(filters=None):
def get_column():
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",
_("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120",
_("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',
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() {
return {
filters: {

View File

@ -444,7 +444,7 @@ def make_rm_stock_entry(purchase_order, rm_items):
item_wh = get_item_details(items)
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.supplier = purchase_order.supplier
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.company = purchase_order.company
stock_entry.to_warehouse = purchase_order.supplier_warehouse
stock_entry.set_stock_entry_type()
for item_code in fg_items:
for rm_item_data in rm_items_list:

View File

@ -465,6 +465,33 @@ class TestPurchaseOrder(unittest.TestCase):
self.assertEquals(se_items, supplied_items)
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):
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."
},
{
"module_name": "Help Desk",
"module_name": "Support",
"category": "Modules",
"label": _("Help Desk"),
"label": _("Support"),
"color": "#1abc9c",
"icon": "fa fa-check-square-o",
"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",
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Expense Claim",
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Loan Type",
@ -296,6 +301,10 @@ def get_data():
"name": "Loan Application",
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Loan"
}
]
},
{

View File

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

View File

@ -10,11 +10,13 @@ def get_data():
"type": "doctype",
"name": "Issue",
"description": _("Support queries from customers."),
"onboard": 1,
},
{
"type": "doctype",
"name": "Communication",
"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"),
"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()
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',
'Supplier Quotation', 'Purchase Receipt', 'Delivery Note', 'Quotation']:
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'):
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 ?
item.set("pricing_rules", ret.get("pricing_rules"))
item.set("discount_percentage", ret.get("discount_percentage"))
@ -546,6 +552,19 @@ class AccountsController(TransactionBase):
from erpnext.accounts.utils import reconcile_against_document
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):
from erpnext.controllers.status_updater import get_tolerance_for
item_tolerance = {}

View File

@ -538,6 +538,8 @@ class BuyingController(StockController):
update_last_purchase_rate(self, is_submit = 1)
def on_cancel(self):
super(BuyingController, self).on_cancel()
if self.get('is_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
from `tabStock Entry` se,`tabStock Entry Detail` sed
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, '') != ''
group by sed.item_code, sed.t_warehouse
""" % (','.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": _("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": _("Lab Test"), "route": "/lab-test", "reference_doctype": "Lab Test", "role":"Patient"},
{"title": _("Prescription"), "route": "/prescription", "reference_doctype": "Patient Encounter", "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.accounts.doctype.gl_entry.gl_entry.rename_gle_sle_docs",
"erpnext.projects.doctype.project.project.hourly_reminder",
"erpnext.projects.doctype.project.project.collect_project_status",
"erpnext.support.doctype.issue.issue.update_support_timer",
"erpnext.projects.doctype.project.project.collect_project_status"
],
"daily": [
"erpnext.stock.reorder_item.reorder_item",

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))
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):
leave_applications = frappe.db.sql("""
select name, employee, leave_type, from_date, to_date, total_leave_days

View File

@ -452,7 +452,10 @@ class SalarySlip(TransactionBase):
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.precision("net_pay") if disable_rounded_total else 0)

View File

@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe
from frappe import _
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):
@ -51,8 +51,7 @@ def get_data(filters, leave_types):
filters.from_date, filters.to_date)
# opening balance
opening = get_leave_balance_on(employee.name, leave_type, filters.from_date,
allocation_records_based_on_from_date.get(employee.name, frappe._dict()))
opening = get_total_allocated_leaves(employee.name, leave_type, filters.to_date)
# closing balance
closing = get_leave_balance_on(employee.name, leave_type, filters.to_date,

View File

@ -14,11 +14,11 @@ def execute(filters=None):
columns, earning_types, ded_types = get_columns(salary_slips)
ss_earning_map = get_ss_earning_map(salary_slips)
ss_ded_map = get_ss_ded_map(salary_slips)
doj_map = get_employee_doj_map()
data = []
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]
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):
"""
columns = [
_("Salary Slip ID") + ":Link/Salary Slip:150",_("Employee") + ":Link/Employee:120", _("Employee Name") + "::140", _("Branch") + ":Link/Branch:120",
_("Department") + ":Link/Department:120", _("Designation") + ":Link/Designation:120",
_("Company") + ":Link/Company:120", _("Start Date") + "::80", _("End Date") + "::80", _("Leave Without Pay") + ":Float:130",
_("Payment Days") + ":Float:120"
_("Salary Slip ID") + ":Link/Salary Slip:150",_("Employee") + ":Link/Employee:120", _("Employee Name") + "::140",
_("Date of Joining") + "::80", _("Branch") + ":Link/Branch:120", _("Department") + ":Link/Department:120",
_("Designation") + ":Link/Designation:120", _("Company") + ":Link/Company:120", _("Start Date") + "::80",
_("End Date") + "::80", _("Leave Without Pay") + ":Float:130", _("Payment Days") + ":Float:120"
]
"""
columns = [
_("Salary Slip ID") + ":Link/Salary Slip:150",_("Employee") + ":Link/Employee:120", _("Employee Name") + "::140", _("Branch") + ":Link/Branch:-1",
_("Department") + ":Link/Department:-1", _("Designation") + ":Link/Designation:-1",
_("Company") + ":Link/Company:120", _("Start Date") + "::80", _("End Date") + "::80", _("Leave Without Pay") + ":Float:-1",
_("Payment Days") + ":Float:120"
_("Salary Slip ID") + ":Link/Salary Slip:150",_("Employee") + ":Link/Employee:120", _("Employee Name") + "::140",
_("Date of Joining") + "::80", _("Branch") + ":Link/Branch:-1", _("Department") + ":Link/Department:-1",
_("Designation") + ":Link/Designation:-1", _("Company") + ":Link/Company:120", _("Start Date") + "::80",
_("End Date") + "::80", _("Leave Without Pay") + ":Float:-1", _("Payment Days") + ":Float:120"
]
salary_components = {_("Earning"): [], _("Deduction"): []}
@ -93,6 +93,16 @@ def get_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):
ss_earnings = frappe.db.sql("""select parent, salary_component, amount
from `tabSalary Detail` where parent in (%s)""" %

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.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.set_stock_entry_type()
if purpose=="Material Transfer for Manufacture":
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)
stock_entry.set("additional_costs", additional_costs)
stock_entry.set_stock_entry_type()
stock_entry.get_items()
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.v11_0.remove_barcodes_field_from_copy_fields_to_variants
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.update_pricing_rule_fields
erpnext.patches.v11_1.make_job_card_time_logs
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
import frappe
def execute():
company = frappe.get_all('Company', filters = {'country': 'Italy'})
if not company:
@ -27,4 +26,12 @@ def execute():
frappe.db.sql("""
UPDATE tabAddress set {condition} country_code = UPPER(ifnull((select code
from `tabCountry` where name = `tabAddress`.country), ''))
where country_code is null and state_code is null
""".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

@ -14,6 +14,6 @@ def execute():
set
sed.subcontracted_item = pois.main_item_code
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.parenttype = 'Purchase Order'""")

View File

@ -3,6 +3,8 @@ import json
from six import iteritems
def execute():
if "tax_type" not in frappe.db.get_table_columns("Item Tax"):
return
old_item_taxes = {}
item_tax_templates = {}
rename_template_to_untitled = []

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()
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()
def check_if_child_exists(name):
child_tasks = frappe.get_all("Task", filters={"parent_task": name})
@ -196,10 +203,9 @@ def set_multiple_status(names, status):
task.save()
def set_tasks_as_overdue():
frappe.db.sql("""update tabTask set `status`='Overdue'
where exp_end_date is not null
and exp_end_date < CURDATE()
and `status` not in ('Completed', 'Cancelled')""")
tasks = frappe.get_all("Task", filters={'status':['not in',['Cancelled', 'Completed']]})
for task in tasks:
frappe.get_doc("Task", task.name).update_status()
@frappe.whitelist()
def get_children(doctype, parent, task=None, project=None, is_root=False):

View File

@ -10,8 +10,8 @@ frappe.ui.form.on("Timesheet", {
filters:{
'status': 'Active'
}
}
}
};
};
frm.fields_dict['time_logs'].grid.get_field('task').get_query = function(frm, cdt, cdn) {
var child = locals[cdt][cdn];
@ -20,22 +20,26 @@ frappe.ui.form.on("Timesheet", {
'project': child.project,
'status': ["!=", "Cancelled"]
}
}
}
};
};
frm.fields_dict['time_logs'].grid.get_field('project').get_query = function() {
return{
filters: {
'company': frm.doc.company
}
}
}
};
};
},
onload: function(frm){
if (frm.doc.__islocal && frm.doc.time_logs) {
calculate_time_and_amount(frm);
}
if (frm.is_new()) {
set_employee_and_company(frm);
}
},
refresh: function(frm) {
@ -58,7 +62,7 @@ frappe.ui.form.on("Timesheet", {
if ((row.from_time <= frappe.datetime.now_datetime()) && !row.completed) {
button = 'Resume Timer';
}
})
});
frm.add_custom_button(__(button), function() {
var flag = true;
@ -77,7 +81,7 @@ frappe.ui.form.on("Timesheet", {
erpnext.timesheet.timer(frm, row, timestamp);
flag = false;
}
})
});
// If no activities found to start a timer, create new
if (flag) {
erpnext.timesheet.timer(frm);
@ -125,8 +129,8 @@ frappe.ui.form.on("Timesheet", {
frappe.set_route("Form", r.message.doctype, r.message.name);
}
}
})
})
});
});
dialog.show();
},
@ -136,7 +140,7 @@ frappe.ui.form.on("Timesheet", {
frm: frm
});
},
})
});
frappe.ui.form.on("Timesheet Detail", {
time_logs_remove: function(frm) {
@ -171,22 +175,22 @@ frappe.ui.form.on("Timesheet Detail", {
.find('[data-fieldname="timer"]')
.append(frappe.render_template("timesheet"));
frm.trigger("control_timer");
})
});
},
hours: function(frm, cdt, cdn) {
calculate_end_time(frm, cdt, cdn)
calculate_end_time(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) {
calculate_billing_costing_amount(frm, cdt, cdn)
calculate_billing_costing_amount(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) {
@ -212,7 +216,7 @@ frappe.ui.form.on("Timesheet Detail", {
calculate_billing_costing_amount(frm, cdt, cdn);
}
}
})
});
}
});
@ -244,19 +248,19 @@ var calculate_end_time = function(frm, cdt, cdn) {
});
}
}
}
};
var update_billing_hours = function(frm, cdt, cdn){
var child = locals[cdt][cdn];
if(!child.billable) frappe.model.set_value(cdt, cdn, 'billing_hours', 0.0);
}
};
var update_time_rates = function(frm, cdt, cdn){
var child = locals[cdt][cdn];
if(!child.billable){
frappe.model.set_value(cdt, cdn, 'billing_rate', 0.0);
}
}
};
var calculate_billing_costing_amount = function(frm, 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, 'costing_amount', costing_amount);
calculate_time_and_amount(frm);
}
};
var calculate_time_and_amount = function(frm) {
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_billable_amount", total_billable_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() {
var me = this;
@ -422,6 +428,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
method: "erpnext.stock.get_item_details.get_item_details",
child: item,
args: {
doc: me.frm.doc,
args: {
item_code: item.item_code,
barcode: item.barcode,
@ -456,7 +463,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
cost_center: item.cost_center,
tax_category: me.frm.doc.tax_category,
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.update_free_items(item)
() => me.validate_pricing_rule(item)
]);
}
}
@ -1116,7 +1123,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
callback: function(r) {
if (!r.exc && r.message) {
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.calculate_taxes_and_totals();
@ -1139,14 +1146,12 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
return this.frm.call({
method: "erpnext.accounts.doctype.pricing_rule.pricing_rule.apply_pricing_rule",
args: { args: args },
args: { args: args, doc: me.frm.doc },
callback: function(r) {
if (!r.exc && r.message) {
me._set_values_for_item_list(r.message);
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")
me.update_free_items(item);
}
}
});
@ -1200,7 +1205,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
"warehouse": d.warehouse,
"serial_no": d.serial_no,
"price_list_rate": d.price_list_rate,
"discount_percentage": d.discount_percentage || 0.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(!me.frm.doc.ignore_pricing_rule && existing_pricing_rule && !d.pricing_rules) {
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) {
var me = this;
validate_pricing_rule: function(item) {
let me = this;
const fields = ["discount_percentage", "discount_amount", "pricing_rules"];
if (item.pricing_rules) {
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: {
pricing_rules: item.pricing_rules,
item_row: item
doc: me.frm.doc
},
callback: function(r) {
let items = [];
let child = '';
me.frm.doc.items.map(d => {
items[d.item_code] = d;
if (r.message) {
r.message.items.forEach(d => {
me.frm.doc.items.forEach(row => {
if(d.name == row.name) {
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,7 +1331,27 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
me.frm.doc.items = 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() {
@ -1379,7 +1393,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
},
callback: function(r) {
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();
}
}

View File

@ -31,7 +31,7 @@
<base-image :src="image" :alt="title" />
</div>
</div>
<div class="col-md-8">
<div class="col-md-8" style='padding-left: 30px;'>
<h2>{{ title }}</h2>
<div class="text-muted">
<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 unittest
from frappe.utils import getdate
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.stock.doctype.item.test_item import make_item
@ -12,10 +13,27 @@ import json
class TestGSTR3BReport(unittest.TestCase):
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.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 `tabGSTR 3B Report` where company='_Test Company GST'")
make_company()
make_item("Milk", properties = {"is_nil_exempt": 1, "standard_rate": 0.000000})
@ -33,8 +51,8 @@ class TestGSTR3BReport(unittest.TestCase):
"doctype": "GSTR 3B Report",
"company": "_Test Company GST",
"company_address": "_Test Address-Billing",
"year": "2019",
"month": "March"
"year": getdate().year,
"month": month_number_mapping.get(getdate().month)
}).insert()
output = json.loads(report.json_output)
@ -55,7 +73,6 @@ def make_sales_invoice():
income_account = 'Sales - _GST',
expense_account = 'Cost of Goods Sold - _GST',
cost_center = 'Main - _GST',
posting_date = '2019-03-10',
do_not_save=1
)
@ -77,7 +94,6 @@ def make_sales_invoice():
income_account = 'Sales - _GST',
expense_account = 'Cost of Goods Sold - _GST',
cost_center = 'Main - _GST',
posting_date = '2019-03-10',
do_not_save=1
)
@ -99,7 +115,6 @@ def make_sales_invoice():
income_account = 'Sales - _GST',
expense_account = 'Cost of Goods Sold - _GST',
cost_center = 'Main - _GST',
posting_date = '2019-03-10',
do_not_save=1
)
@ -122,7 +137,6 @@ def make_sales_invoice():
income_account = 'Sales - _GST',
expense_account = 'Cost of Goods Sold - _GST',
cost_center = 'Main - _GST',
posting_date = '2019-03-10',
do_not_save=1
)
si3.submit()
@ -135,7 +149,6 @@ def create_purchase_invoices():
currency = 'INR',
warehouse = 'Finished Goods - _GST',
cost_center = 'Main - _GST',
posting_date = '2019-03-10',
do_not_save=1,
)
@ -157,7 +170,6 @@ def create_purchase_invoices():
currency = 'INR',
warehouse = 'Finished Goods - _GST',
cost_center = 'Main - _GST',
posting_date = '2019-03-10',
item = "Milk",
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 Receipt 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': [
dict(fieldname='ifsc_code', label='IFSC Code',
fieldtype='Data', insert_after='bank_ac_no', print_hide=1,

View File

@ -94,14 +94,13 @@
<Nome>{{ doc.customer_data.first_name }}</Nome>
<Cognome>{{ doc.customer_data.last_name }}</Cognome>
</Anagrafica>
{%- else %}
{%- if doc.customer_data.is_public_administration %}
<CodiceFiscale>{{ doc.customer_data.fiscal_code }}</CodiceFiscale>
{%- else %}
<IdFiscaleIVA>
<IdPaese>{{ doc.customer_address_data.country_code }}</IdPaese>
<IdCodice>{{ doc.tax_id | replace("IT","") }}</IdCodice>
</IdFiscaleIVA>
{%- if doc.customer_data.fiscal_code %}
<CodiceFiscale>{{ doc.customer_data.fiscal_code }}</CodiceFiscale>
{%- endif %}
<Anagrafica>
<Denominazione>{{ doc.customer_name }}</Denominazione>
@ -128,22 +127,42 @@
<ImportoBollo>{{ format_float(doc.stamp_duty) }}</ImportoBollo>
</DatiBollo>
{%- 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>
</DatiGeneraliDocumento>
{%- if doc.po_no %}
{%- for po_no, po_date in doc.customer_po_data.items() %}
<DatiOrdineAcquisto>
<IdDocumento>{{ doc.po_no }}</IdDocumento>
{%- if doc.po_date %}
<Data>{{ doc.po_date }}</Data>
{%- endif %}
<IdDocumento>{{ po_no }}</IdDocumento>
<Data>{{ po_date }}</Data>
</DatiOrdineAcquisto>
{%- endif %}
{%- endfor %}
{%- if doc.is_return and doc.return_against_unamended %}
<DatiFattureCollegate>
<IdDocumento>{{ doc.return_against_unamended }}</IdDocumento>
</DatiFattureCollegate>
{%- 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 %}
<DatiTrasporto>
<IndirizzoResa>
@ -165,7 +184,11 @@
<UnitaMisura>{{ item.stock_uom }}</UnitaMisura>
<PrezzoUnitario>{{ format_float(item.price_list_rate or item.rate) }}</PrezzoUnitario>
{{ render_discount_or_margin(item) }}
{%- 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>
{%- if item.tax_exemption_reason %}
<Natura>{{ item.tax_exemption_reason.split("-")[0] }}</Natura>
@ -199,7 +222,9 @@
<ModalitaPagamento>{{ payment_term.mode_of_payment_code.split("-")[0] }}</ModalitaPagamento>
<DataScadenzaPagamento>{{ payment_term.due_date }}</DataScadenzaPagamento>
<ImportoPagamento>{{ format_float(payment_term.payment_amount) }}</ImportoPagamento>
{%- if payment_term.bank_account_name %}
<IstitutoFinanziario>{{ payment_term.bank_account_name }}</IstitutoFinanziario>
{%- endif %}
{%- if payment_term.bank_account_iban %}
<IBAN>{{ payment_term.bank_account_iban }}</IBAN>
<ABI>{{ payment_term.bank_account_iban[5:10] }}</ABI>

View File

@ -3,15 +3,26 @@ erpnext.setup_e_invoice_button = (doctype) => {
refresh: (frm) => {
if(frm.doc.docstatus == 1) {
frm.add_custom_button('Generate E-Invoice', () => {
frm.call({
method: "erpnext.regional.italy.utils.generate_single_invoice",
args: {
docname: frm.doc.name
},
callback: function(r) {
frm.reload_doc();
if(r.message) {
var w = window.open(
frappe.urllib.get_full_url(
"/api/method/erpnext.regional.italy.utils.generate_single_invoice?"
+ "docname=" + frm.doc.name
"/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")
]
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 = {
'Company': [
dict(fieldname='sb_e_invoicing', label='E-Invoicing',
@ -128,7 +144,7 @@ def make_custom_fields(update=True):
'Purchase Invoice Item': invoice_item_fields,
'Sales Order 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,
'Purchase Order 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 frappe import _
from frappe.core.doctype.file.file import remove_file
from six import string_types
from frappe.desk.form.load import get_attachments
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:
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
def get_conditions(filters):
@ -134,6 +143,7 @@ def get_invoice_summary(items, taxes):
idx=len(items)+1,
item_code=reference_row.description,
item_name=reference_row.description,
description=reference_row.description,
rate=reference_row.tax_amount,
qty=1.0,
amount=reference_row.tax_amount,
@ -142,7 +152,7 @@ def get_invoice_summary(items, taxes):
tax_amount=(reference_row.tax_amount * tax.rate) / 100,
net_amount=reference_row.tax_amount,
taxable_amount=reference_row.tax_amount,
item_tax_rate="{}",
item_tax_rate={tax.account_head: tax.rate},
charges=True
)
)
@ -150,10 +160,16 @@ def get_invoice_summary(items, taxes):
#Check item tax rates if tax rate is zero.
if tax.rate == 0:
for item in items:
item_tax_rate = item.item_tax_rate
if isinstance(item.item_tax_rate, string_types):
item_tax_rate = json.loads(item.item_tax_rate)
if tax.account_head in item_tax_rate:
if item_tax_rate and tax.account_head in item_tax_rate:
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]["taxable_amount"] += item.net_amount
if key == "0.0":
@ -198,19 +214,25 @@ def sales_invoice_validate(doc):
else:
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:
frappe.throw(_("Please set either the Tax ID or Fiscal Code on Company '%s'" % doc.company), title=_("E-Invoicing Information Missing"))
#Validate customer details
customer_type, is_public_administration = frappe.db.get_value("Customer", doc.customer, ["customer_type", "is_public_administration"])
if customer_type == _("Individual"):
customer = frappe.get_doc("Customer", doc.customer)
if customer.customer_type == _("Individual"):
doc.customer_fiscal_code = 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"))
else:
if is_public_administration:
if customer.is_public_administration:
doc.customer_fiscal_code = 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"))
else:
doc.tax_id = customer.tax_id
if not doc.tax_id:
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):
doc = frappe.get_doc("Sales Invoice", docname)
e_invoice = prepare_and_attach_invoice(doc, True)
return e_invoice.file_name
@frappe.whitelist()
def download_e_invoice_file(file_name):
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()
frappe.local.response.filename = e_invoice.file_name
frappe.local.response.filename = file_name
frappe.local.response.filecontent = content
frappe.local.response.type = "download"

View File

@ -49,5 +49,20 @@ frappe.query_reports["GSTR-1"] = {
"options": ["B2B", "B2C Large", "B2C Small", "CDNR", "EXPORT"],
"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
import frappe, json
from frappe import _
from frappe.utils import flt, formatdate
from frappe.utils import flt, formatdate, now_datetime, getdate
from datetime import date
from six import iteritems
from erpnext.regional.doctype.gstr_3b_report.gstr_3b_report import get_period
def execute(filters=None):
return Gstr1Report(filters).run()
@ -38,7 +39,7 @@ class Gstr1Report(object):
shipping_bill_date,
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):
self.get_columns()
@ -113,9 +114,14 @@ class Gstr1Report(object):
if self.filters.get(opts[0]):
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":
customers = frappe.get_all("Customer",
filters={
"gst_category": ["in", ["Registered Regular", "Deemed Export", "SEZ"]]
})
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]))
@ -124,6 +130,11 @@ class Gstr1Report(object):
if not b2c_limit:
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":
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})""".\
@ -494,3 +505,158 @@ class Gstr1Report(object):
}
]
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()
def on_cancel(self):
super(Quotation, self).on_cancel()
#update enquiry status
self.set_status(update=True)
self.update_opportunity()

View File

@ -183,6 +183,8 @@ class SalesOrder(SellingController):
self.update_blanket_order()
def on_cancel(self):
super(SalesOrder, self).on_cancel()
# Cannot cancel closed SO
if self.status == 'Closed':
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
from __future__ import unicode_literals
import frappe
from frappe.utils import flt, add_days
from frappe.utils import flt, add_days, nowdate
import frappe.permissions
import unittest
from erpnext.selling.doctype.sales_order.sales_order \
@ -13,7 +13,6 @@ from erpnext.controllers.accounts_controller import update_child_qty_rate
import json
from erpnext.selling.doctype.sales_order.sales_order import make_raw_material_request
class TestSalesOrder(unittest.TestCase):
def tearDown(self):
frappe.set_user("Administrator")
@ -710,6 +709,28 @@ class TestSalesOrder(unittest.TestCase):
se.cancel()
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):
from erpnext.stock.doctype.item.test_item import make_item
item = make_item("_Test Finished Item", {"is_stock_item": 1,

View File

@ -233,9 +233,21 @@ erpnext.pos.PointOfSale = class PointOfSale {
} else {
this.update_item_in_frm(item, field, value)
.then(() => {
frappe.dom.unfreeze();
frappe.run_serially([
() => {
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.update_cart_data(item);
this.set_form_action();
this.on_qty_change(item_row);
});
} else {
this.on_qty_change(item);
}
},
() => this.post_qty_change(item)
]);
});
}
return;
@ -251,7 +263,28 @@ erpnext.pos.PointOfSale = class PointOfSale {
frappe.flags.hide_serial_batch_dialog = true;
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;
@ -261,14 +294,25 @@ erpnext.pos.PointOfSale = class PointOfSale {
(item.has_serial_no) || (item.actual_batch_qty != item.actual_qty)) ) {
// check has serial no/batch no and update cart
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) {
frappe.dom.unfreeze();
@ -283,7 +327,8 @@ erpnext.pos.PointOfSale = class PointOfSale {
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) {
this.cart.add_item(item);
this.cart.update_taxes_and_totals();
this.cart.update_grand_total();
this.cart.update_qty_total();
frappe.dom.unfreeze();
}
@ -446,16 +488,15 @@ erpnext.pos.PointOfSale = class PointOfSale {
}
setup_company() {
this.company = frappe.sys_defaults.company;
return new Promise(resolve => {
if(!this.company) {
if(!frappe.sys_defaults.company) {
frappe.prompt({fieldname:"company", options: "Company", fieldtype:"Link",
label: __("Select Company"), reqd: 1}, (data) => {
this.company = data.company;
resolve(this.company);
}, __("Select Company"));
} else {
resolve(this.company);
resolve();
}
})
}
@ -509,7 +550,9 @@ erpnext.pos.PointOfSale = class PointOfSale {
}
set_pos_profile_data() {
if (this.company) {
this.frm.doc.company = this.company;
}
return new Promise(resolve => {
return this.frm.call({
@ -953,7 +996,6 @@ class POSCart {
$item.appendTo(this.$cart_items);
}
this.highlight_item(item.item_code);
this.scroll_to_item(item.item_code);
}
update_item(item) {
@ -1206,7 +1248,10 @@ class POSItems {
clearTimeout(this.last_search);
this.last_search = setTimeout(() => {
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);
});

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 ""
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:
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
(start=start,page_length=page_length,lft=lft, rgt=rgt, condition=condition),
{
'item_code': item_code,
'price_list': price_list,
'warehouse': warehouse
} , 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):
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
or i.item_name like %(item_code)s)"""
return frappe.db.escape('%' + item_code + '%'), condition
return """(i.name like {item_code}
or i.item_name like {item_code})""".format(item_code = frappe.db.escape('%' + item_code + '%'))
def get_item_group_condition(pos_profile):
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
/* eslint-disable */
frappe.query_reports["Sales Partner-wise Transaction Summary"] = {
frappe.query_reports["Sales Partner Transaction Summary"] = {
"filters": [
{
fieldname: "sales_partner",

View File

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

View File

@ -1,8 +1,16 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// 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": [
{
fieldname:"company",
label: __("Company"),
fieldtype: "Link",
options: "Company",
default: frappe.defaults.get_user_default("Company")
},
{
fieldname: "fiscal_year",
label: __("Fiscal Year"),
@ -10,6 +18,13 @@ frappe.query_reports["Territory Target Variance Item Group-Wise"] = {
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"),

View File

@ -1,19 +1,21 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2013-06-21 12:15:00",
"creation": "2019-03-25 22:16:49.040998",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 3,
"idx": 0,
"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",
"module": "Selling",
"name": "Territory Target Variance Item Group-Wise",
"name": "Sales Person Target Variance Based On Item Group",
"owner": "Administrator",
"prepared_report": 0,
"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",
"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
// License: GNU General Public License v3. See license.txt
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// 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": [
{
fieldname:"company",
label: __("Company"),
fieldtype: "Link",
options: "Company",
default: frappe.defaults.get_user_default("Company")
},
{
fieldname: "fiscal_year",
label: __("Fiscal Year"),
@ -10,6 +18,13 @@ frappe.query_reports["Sales Person Target Variance Item Group-Wise"] = {
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"),

View File

@ -1,19 +1,21 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2013-06-21 12:14:15",
"creation": "2019-03-25 22:20:59.033199",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 3,
"idx": 0,
"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",
"module": "Selling",
"name": "Sales Person Target Variance Item Group-Wise",
"name": "Territory Target Variance Based On Item Group",
"owner": "Administrator",
"prepared_report": 0,
"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",
"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

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

View File

@ -13,5 +13,16 @@ frappe.ui.form.on('Sales Partner', {
unhide_field(['address_html', 'contact_html', 'address_contacts']);
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_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
@ -21,6 +22,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "partner_name",
"fieldtype": "Data",
"hidden": 0,
@ -45,7 +47,7 @@
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"unique": 1
},
{
"allow_bulk_edit": 0,
@ -54,6 +56,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "partner_type",
"fieldtype": "Link",
"hidden": 0,
@ -89,6 +92,7 @@
"collapsible": 0,
"columns": 0,
"description": "",
"fetch_if_empty": 0,
"fieldname": "territory",
"fieldtype": "Link",
"hidden": 0,
@ -121,6 +125,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break0",
"fieldtype": "Column Break",
"hidden": 0,
@ -153,6 +158,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "commission_rate",
"fieldtype": "Float",
"hidden": 0,
@ -186,6 +192,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "address_contacts",
"fieldtype": "Section Break",
"hidden": 0,
@ -218,6 +225,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.__islocal",
"fetch_if_empty": 0,
"fieldname": "address_desc",
"fieldtype": "HTML",
"hidden": 0,
@ -249,6 +257,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "address_html",
"fieldtype": "HTML",
"hidden": 0,
@ -280,6 +289,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break1",
"fieldtype": "Column Break",
"hidden": 0,
@ -311,6 +321,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.__islocal",
"fetch_if_empty": 0,
"fieldname": "contact_desc",
"fieldtype": "HTML",
"hidden": 0,
@ -342,6 +353,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "contact_html",
"fieldtype": "HTML",
"hidden": 0,
@ -373,6 +385,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "partner_target_details_section_break",
"fieldtype": "Section Break",
"hidden": 0,
@ -405,6 +418,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "targets",
"fieldtype": "Table",
"hidden": 0,
@ -439,41 +453,7 @@
"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
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "website",
"fieldtype": "Section Break",
"hidden": 0,
@ -505,6 +485,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "show_in_website",
"fieldtype": "Check",
"hidden": 0,
@ -537,6 +518,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "show_in_website",
"fetch_if_empty": 0,
"fieldname": "section_break_17",
"fieldtype": "Section Break",
"hidden": 0,
@ -567,6 +549,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "route",
"fieldtype": "Data",
"hidden": 0,
@ -599,6 +582,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_20",
"fieldtype": "Column Break",
"hidden": 0,
@ -629,6 +613,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "logo",
"fieldtype": "Attach",
"hidden": 0,
@ -661,6 +646,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "partner_website",
"fieldtype": "Data",
"hidden": 0,
@ -693,6 +679,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "show_in_website",
"fetch_if_empty": 0,
"fieldname": "section_break_22",
"fieldtype": "Section Break",
"hidden": 0,
@ -723,6 +710,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "introduction",
"fieldtype": "Text",
"hidden": 0,
@ -754,6 +742,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "description",
"fieldtype": "Text Editor",
"hidden": 0,
@ -790,7 +779,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-06-11 13:47:04.182339",
"modified": "2019-03-21 16:26:45.447265",
"modified_by": "Administrator",
"module": "Setup",
"name": "Sales Partner",
@ -860,5 +849,6 @@
"show_name_in_global_search": 1,
"sort_order": "ASC",
"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}',
[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,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "name_and_employee_id",
"fieldtype": "Section Break",
"hidden": 0,
@ -54,6 +55,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "sales_person_name",
"fieldtype": "Data",
"hidden": 0,
@ -88,6 +90,7 @@
"collapsible": 0,
"columns": 0,
"description": "Select company name first.",
"fetch_if_empty": 0,
"fieldname": "parent_sales_person",
"fieldtype": "Link",
"hidden": 0,
@ -122,6 +125,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "commission_rate",
"fieldtype": "Data",
"hidden": 0,
@ -154,6 +158,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "is_group",
"fieldtype": "Check",
"hidden": 0,
@ -189,6 +194,7 @@
"collapsible": 0,
"columns": 0,
"default": "1",
"fetch_if_empty": 0,
"fieldname": "enabled",
"fieldtype": "Check",
"hidden": 0,
@ -221,6 +227,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "cb0",
"fieldtype": "Column Break",
"hidden": 0,
@ -251,6 +258,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "employee",
"fieldtype": "Link",
"hidden": 0,
@ -284,6 +292,7 @@
"collapsible": 0,
"columns": 0,
"fetch_from": "employee.department",
"fetch_if_empty": 0,
"fieldname": "department",
"fieldtype": "Link",
"hidden": 0,
@ -317,6 +326,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "lft",
"fieldtype": "Int",
"hidden": 1,
@ -350,6 +360,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "rgt",
"fieldtype": "Int",
"hidden": 1,
@ -383,6 +394,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "old_parent",
"fieldtype": "Data",
"hidden": 1,
@ -417,6 +429,7 @@
"collapsible": 0,
"columns": 0,
"description": "Set targets Item Group-wise for this Sales Person.",
"fetch_if_empty": 0,
"fieldname": "target_details_section_break",
"fieldtype": "Section Break",
"hidden": 0,
@ -450,6 +463,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "targets",
"fieldtype": "Table",
"hidden": 0,
@ -476,41 +490,6 @@
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "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,
@ -524,7 +503,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-01-30 11:28:16.966735",
"modified": "2019-03-21 16:26:01.706129",
"modified_by": "Administrator",
"module": "Setup",
"name": "Sales Person",

View File

@ -19,6 +19,7 @@
"collapsible": 0,
"columns": 0,
"description": "",
"fetch_if_empty": 0,
"fieldname": "item_group",
"fieldtype": "Link",
"hidden": 0,
@ -53,6 +54,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "fiscal_year",
"fieldtype": "Link",
"hidden": 0,
@ -87,6 +89,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "target_qty",
"fieldtype": "Float",
"hidden": 0,
@ -120,6 +123,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "target_amount",
"fieldtype": "Float",
"hidden": 0,
@ -145,6 +149,40 @@
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "distribution_id",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Target Distribution",
"length": 0,
"no_copy": 0,
"options": "Monthly Distribution",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
@ -157,7 +195,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2019-01-07 16:56:48.179709",
"modified": "2019-03-20 16:59:03.578274",
"modified_by": "Administrator",
"module": "Setup",
"name": "Target Detail",

View File

@ -1,6 +1,18 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.ui.form.on("Territory", {
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
}
}
};
}
});
cur_frm.cscript.refresh = function(doc, cdt, cdn) {
cur_frm.cscript.set_root_readonly(doc);

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