Merge branch 'develop' of github.com:frappe/erpnext into general-ui-fixes
This commit is contained in:
commit
146be9f6a6
@ -4,6 +4,10 @@
|
||||
"node": true,
|
||||
"es6": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"rules": {
|
||||
"indent": [
|
||||
@ -50,9 +54,9 @@
|
||||
"root": true,
|
||||
"globals": {
|
||||
"frappe": true,
|
||||
"Vue": true,
|
||||
"erpnext": true,
|
||||
"hub": true,
|
||||
|
||||
"$": true,
|
||||
"jQuery": true,
|
||||
"moment": true,
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -12,3 +12,4 @@ erpnext/docs/current
|
||||
*.swo
|
||||
__pycache__
|
||||
*~
|
||||
.idea/
|
@ -74,7 +74,7 @@ def is_perpetual_inventory_enabled(company):
|
||||
frappe.local.enable_perpetual_inventory = {}
|
||||
|
||||
if not company in frappe.local.enable_perpetual_inventory:
|
||||
frappe.local.enable_perpetual_inventory[company] = frappe.get_cached_value('Company',
|
||||
frappe.local.enable_perpetual_inventory[company] = frappe.get_cached_value('Company',
|
||||
company, "enable_perpetual_inventory") or 0
|
||||
|
||||
return frappe.local.enable_perpetual_inventory[company]
|
||||
@ -87,7 +87,7 @@ def get_default_finance_book(company=None):
|
||||
frappe.local.default_finance_book = {}
|
||||
|
||||
if not company in frappe.local.default_finance_book:
|
||||
frappe.local.default_finance_book[company] = frappe.get_cached_value('Company',
|
||||
frappe.local.default_finance_book[company] = frappe.get_cached_value('Company',
|
||||
company, "default_finance_book")
|
||||
|
||||
return frappe.local.default_finance_book[company]
|
||||
@ -108,7 +108,7 @@ def get_region(company=None):
|
||||
You can also set global company flag in `frappe.flags.company`
|
||||
'''
|
||||
if company or frappe.flags.company:
|
||||
return frappe.get_cached_value('Company',
|
||||
return frappe.get_cached_value('Company',
|
||||
company or frappe.flags.company, 'country')
|
||||
elif frappe.flags.country:
|
||||
return frappe.flags.country
|
||||
@ -144,4 +144,4 @@ def is_member():
|
||||
last_membership = get_last_membership()
|
||||
if last_membership and getdate(last_membership.to_date) > getdate():
|
||||
return True
|
||||
return False
|
||||
return False
|
||||
|
@ -29,7 +29,7 @@ def validate_service_stop_date(doc):
|
||||
if date_diff(item.service_stop_date, item.service_end_date) > 0:
|
||||
frappe.throw(_("Service Stop Date cannot be after Service End Date"))
|
||||
|
||||
if old_stop_dates and old_stop_dates[item.name] and item.service_stop_date!=old_stop_dates[item.name]:
|
||||
if old_stop_dates and old_stop_dates.get(item.name) and item.service_stop_date!=old_stop_dates.get(item.name):
|
||||
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):
|
||||
|
@ -406,9 +406,9 @@ def get_transaction_entries(filename, headers):
|
||||
from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file
|
||||
rows = read_xlsx_file_from_attached_file(file_id=filename)
|
||||
elif (filename.lower().endswith("csv")):
|
||||
from frappe.utils.file_manager import get_file_path
|
||||
from frappe.utils.csvutils import read_csv_content
|
||||
filepath = get_file_path(filename)
|
||||
_file = frappe.get_doc("File", {"file_name": filename})
|
||||
filepath = _file.get_full_path()
|
||||
with open(filepath,'rb') as csvfile:
|
||||
rows = read_csv_content(csvfile.read())
|
||||
elif (filename.lower().endswith("xls")):
|
||||
@ -428,8 +428,8 @@ def get_transaction_entries(filename, headers):
|
||||
return transactions
|
||||
|
||||
def get_rows_from_xls_file(filename):
|
||||
from frappe.utils.file_manager import get_file_path
|
||||
filepath = get_file_path(filename)
|
||||
_file = frappe.get_doc("File", {"file_name": filename})
|
||||
filepath = _file.get_full_path()
|
||||
import xlrd
|
||||
book = xlrd.open_workbook(filepath)
|
||||
sheets = book.sheets()
|
||||
|
@ -15,7 +15,7 @@ class DuplicateBudgetError(frappe.ValidationError): pass
|
||||
|
||||
class Budget(Document):
|
||||
def autoname(self):
|
||||
self.name = make_autoname(self.get(frappe.scrub(self.budget_against))
|
||||
self.name = make_autoname(self.get(frappe.scrub(self.budget_against))
|
||||
+ "/" + self.fiscal_year + "/.###")
|
||||
|
||||
def validate(self):
|
||||
@ -89,7 +89,7 @@ def validate_expense_against_budget(args):
|
||||
|
||||
if args.get('company') and not args.fiscal_year:
|
||||
args.fiscal_year = get_fiscal_year(args.get('posting_date'), company=args.get('company'))[0]
|
||||
frappe.flags.exception_approver_role = frappe.get_cached_value('Company',
|
||||
frappe.flags.exception_approver_role = frappe.get_cached_value('Company',
|
||||
args.get('company'), 'exception_budget_approver_role')
|
||||
|
||||
if not args.account:
|
||||
@ -106,12 +106,12 @@ def validate_expense_against_budget(args):
|
||||
and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"})):
|
||||
|
||||
if args.project and budget_against == 'project':
|
||||
condition = "and b.project='%s'" % frappe.db.escape(args.project)
|
||||
condition = "and b.project=%s" % frappe.db.escape(args.project)
|
||||
args.budget_against_field = "Project"
|
||||
|
||||
|
||||
elif args.cost_center and budget_against == 'cost_center':
|
||||
cc_lft, cc_rgt = frappe.db.get_value("Cost Center", args.cost_center, ["lft", "rgt"])
|
||||
condition = """and exists(select name from `tabCost Center`
|
||||
condition = """and exists(select name from `tabCost Center`
|
||||
where lft<=%s and rgt>=%s and name=b.cost_center)""" % (cc_lft, cc_rgt)
|
||||
args.budget_against_field = "Cost Center"
|
||||
|
||||
@ -126,13 +126,13 @@ def validate_expense_against_budget(args):
|
||||
b.action_if_annual_budget_exceeded, b.action_if_accumulated_monthly_budget_exceeded,
|
||||
b.action_if_annual_budget_exceeded_on_mr, b.action_if_accumulated_monthly_budget_exceeded_on_mr,
|
||||
b.action_if_annual_budget_exceeded_on_po, b.action_if_accumulated_monthly_budget_exceeded_on_po
|
||||
from
|
||||
from
|
||||
`tabBudget` b, `tabBudget Account` ba
|
||||
where
|
||||
b.name=ba.parent and b.fiscal_year=%s
|
||||
b.name=ba.parent and b.fiscal_year=%s
|
||||
and ba.account=%s and b.docstatus=1
|
||||
{condition}
|
||||
""".format(condition=condition,
|
||||
""".format(condition=condition,
|
||||
budget_against_field=frappe.scrub(args.get("budget_against_field"))),
|
||||
(args.fiscal_year, args.account), as_dict=True)
|
||||
|
||||
@ -151,12 +151,12 @@ def validate_budget_records(args, budget_records):
|
||||
|
||||
args["month_end_date"] = get_last_day(args.posting_date)
|
||||
|
||||
compare_expense_with_budget(args, budget_amount,
|
||||
compare_expense_with_budget(args, budget_amount,
|
||||
_("Accumulated Monthly"), monthly_action, budget.budget_against, amount)
|
||||
|
||||
if yearly_action in ("Stop", "Warn") and monthly_action != "Stop" \
|
||||
and yearly_action != monthly_action:
|
||||
compare_expense_with_budget(args, flt(budget.budget_amount),
|
||||
compare_expense_with_budget(args, flt(budget.budget_amount),
|
||||
_("Annual"), yearly_action, budget.budget_against, amount)
|
||||
|
||||
def compare_expense_with_budget(args, budget_amount, action_for, action, budget_against, amount=0):
|
||||
@ -166,9 +166,9 @@ def compare_expense_with_budget(args, budget_amount, action_for, action, budget_
|
||||
currency = frappe.get_cached_value('Company', args.company, 'default_currency')
|
||||
|
||||
msg = _("{0} Budget for Account {1} against {2} {3} is {4}. It will exceed by {5}").format(
|
||||
_(action_for), frappe.bold(args.account), args.budget_against_field,
|
||||
_(action_for), frappe.bold(args.account), args.budget_against_field,
|
||||
frappe.bold(budget_against),
|
||||
frappe.bold(fmt_money(budget_amount, currency=currency)),
|
||||
frappe.bold(fmt_money(budget_amount, currency=currency)),
|
||||
frappe.bold(fmt_money(diff, currency=currency)))
|
||||
|
||||
if (frappe.flags.exception_approver_role
|
||||
@ -250,12 +250,12 @@ def get_actual_expense(args):
|
||||
condition1 = " and gle.posting_date <= %(month_end_date)s" \
|
||||
if args.get("month_end_date") else ""
|
||||
if args.budget_against_field == "Cost Center":
|
||||
lft_rgt = frappe.db.get_value(args.budget_against_field,
|
||||
lft_rgt = frappe.db.get_value(args.budget_against_field,
|
||||
args.budget_against, ["lft", "rgt"], as_dict=1)
|
||||
args.update(lft_rgt)
|
||||
condition2 = """and exists(select name from `tabCost Center`
|
||||
condition2 = """and exists(select name from `tabCost Center`
|
||||
where lft>=%(lft)s and rgt<=%(rgt)s and name=gle.cost_center)"""
|
||||
|
||||
|
||||
elif args.budget_against_field == "Project":
|
||||
condition2 = "and exists(select name from `tabProject` where name=gle.project and gle.project = %(budget_against)s)"
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Cashier-closing-",
|
||||
"default": "POS-CLO-",
|
||||
"fieldname": "naming_series",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
@ -32,7 +32,7 @@
|
||||
"label": "Series",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Cashier-closing-\n",
|
||||
"options": "POS-CLO-",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
@ -230,6 +230,37 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "0.00",
|
||||
"fieldname": "returns",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 1,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Returns",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "2",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@ -364,7 +395,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-09-03 10:59:54.500567",
|
||||
"modified": "2018-10-21 14:26:15.812416",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Cashier Closing",
|
||||
@ -400,4 +431,4 @@
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ class CashierClosing(Document):
|
||||
for i in self.payments:
|
||||
total += flt(i.amount)
|
||||
|
||||
self.net_amount = total + self.outstanding_amount + self.expense - self.custody
|
||||
self.net_amount = total + self.outstanding_amount + self.expense - self.custody + self.returns
|
||||
|
||||
def validate_time(self):
|
||||
if self.from_time >= self.time:
|
||||
|
@ -162,7 +162,7 @@ def check_freezing_date(posting_date, adv_adj=False):
|
||||
|
||||
def update_outstanding_amt(account, party_type, party, against_voucher_type, against_voucher, on_cancel=False):
|
||||
if party_type and party:
|
||||
party_condition = " and party_type='{0}' and party='{1}'"\
|
||||
party_condition = " and party_type={0} and party={1}"\
|
||||
.format(frappe.db.escape(party_type), frappe.db.escape(party))
|
||||
else:
|
||||
party_condition = ""
|
||||
|
@ -800,7 +800,7 @@ def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
|
||||
from `tabJournal Entry` jv, `tabJournal Entry Account` jv_detail
|
||||
where jv_detail.parent = jv.name and jv_detail.account = %s and ifnull(jv_detail.party, '') = %s
|
||||
and (jv_detail.reference_type is null or jv_detail.reference_type = '')
|
||||
and jv.docstatus = 1 and jv.`{0}` like %s order by jv.name desc limit %s, %s""".format(frappe.db.escape(searchfield)),
|
||||
and jv.docstatus = 1 and jv.`{0}` like %s order by jv.name desc limit %s, %s""".format(searchfield),
|
||||
(filters.get("account"), cstr(filters.get("party")), "%{0}%".format(txt), start, page_len))
|
||||
|
||||
|
||||
|
@ -19,7 +19,7 @@ def get_loyalty_details(customer, loyalty_program, expiry_date=None, company=Non
|
||||
|
||||
condition = ''
|
||||
if company:
|
||||
condition = " and company='%s' " % frappe.db.escape(company)
|
||||
condition = " and company=%s " % frappe.db.escape(company)
|
||||
if not include_expired_entry:
|
||||
condition += " and expiry_date>='%s' " % expiry_date
|
||||
|
||||
|
@ -549,7 +549,7 @@ def get_outstanding_reference_documents(args):
|
||||
# Get positive outstanding sales /purchase invoices/ Fees
|
||||
condition = ""
|
||||
if args.get("voucher_type") and args.get("voucher_no"):
|
||||
condition = " and voucher_type='{0}' and voucher_no='{1}'"\
|
||||
condition = " and voucher_type={0} and voucher_no={1}"\
|
||||
.format(frappe.db.escape(args["voucher_type"]), frappe.db.escape(args["voucher_no"]))
|
||||
|
||||
# Add cost center condition
|
||||
|
@ -13,20 +13,20 @@ class PaymentReconciliation(Document):
|
||||
def get_unreconciled_entries(self):
|
||||
self.get_nonreconciled_payment_entries()
|
||||
self.get_invoice_entries()
|
||||
|
||||
|
||||
def get_nonreconciled_payment_entries(self):
|
||||
self.check_mandatory_to_fetch()
|
||||
|
||||
|
||||
payment_entries = self.get_payment_entries()
|
||||
journal_entries = self.get_jv_entries()
|
||||
|
||||
|
||||
self.add_payment_entries(payment_entries + journal_entries)
|
||||
|
||||
|
||||
def get_payment_entries(self):
|
||||
order_doctype = "Sales Order" if self.party_type=="Customer" else "Purchase Order"
|
||||
payment_entries = get_advance_payment_entries(self.party_type, self.party,
|
||||
payment_entries = get_advance_payment_entries(self.party_type, self.party,
|
||||
self.receivable_payable_account, order_doctype, against_all_orders=True)
|
||||
|
||||
|
||||
return payment_entries
|
||||
|
||||
def get_jv_entries(self):
|
||||
@ -38,8 +38,8 @@ class PaymentReconciliation(Document):
|
||||
|
||||
journal_entries = frappe.db.sql("""
|
||||
select
|
||||
"Journal Entry" as reference_type, t1.name as reference_name,
|
||||
t1.posting_date, t1.remark as remarks, t2.name as reference_row,
|
||||
"Journal Entry" as reference_type, t1.name as reference_name,
|
||||
t1.posting_date, t1.remark as remarks, t2.name as reference_row,
|
||||
{dr_or_cr} as amount, t2.is_advance
|
||||
from
|
||||
`tabJournal Entry` t1, `tabJournal Entry Account` t2
|
||||
@ -47,8 +47,8 @@ class PaymentReconciliation(Document):
|
||||
t1.name = t2.parent and t1.docstatus = 1 and t2.docstatus = 1
|
||||
and t2.party_type = %(party_type)s and t2.party = %(party)s
|
||||
and t2.account = %(account)s and {dr_or_cr} > 0
|
||||
and (t2.reference_type is null or t2.reference_type = '' or
|
||||
(t2.reference_type in ('Sales Order', 'Purchase Order')
|
||||
and (t2.reference_type is null or t2.reference_type = '' or
|
||||
(t2.reference_type in ('Sales Order', 'Purchase Order')
|
||||
and t2.reference_name is not null and t2.reference_name != ''))
|
||||
and (CASE
|
||||
WHEN t1.voucher_type in ('Debit Note', 'Credit Note')
|
||||
@ -106,7 +106,7 @@ class PaymentReconciliation(Document):
|
||||
self.validate_invoice()
|
||||
dr_or_cr = ("credit_in_account_currency"
|
||||
if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit_in_account_currency")
|
||||
|
||||
|
||||
lst = []
|
||||
for e in self.get('payments'):
|
||||
if e.invoice_number and e.allocated_amount:
|
||||
@ -124,11 +124,11 @@ class PaymentReconciliation(Document):
|
||||
'unadjusted_amount' : flt(e.amount),
|
||||
'allocated_amount' : flt(e.allocated_amount)
|
||||
}))
|
||||
|
||||
|
||||
if lst:
|
||||
from erpnext.accounts.utils import reconcile_against_document
|
||||
reconcile_against_document(lst)
|
||||
|
||||
|
||||
msgprint(_("Successfully Reconciled"))
|
||||
self.get_unreconciled_entries()
|
||||
|
||||
@ -171,8 +171,8 @@ class PaymentReconciliation(Document):
|
||||
frappe.throw(_("Please select Allocated Amount, Invoice Type and Invoice Number in atleast one row"))
|
||||
|
||||
def check_condition(self):
|
||||
cond = " and posting_date >= '{0}'".format(frappe.db.escape(self.from_date)) if self.from_date else ""
|
||||
cond += " and posting_date <= '{0}'".format(frappe.db.escape(self.to_date)) if self.to_date else ""
|
||||
cond = " and posting_date >= {0}".format(frappe.db.escape(self.from_date)) if self.from_date else ""
|
||||
cond += " and posting_date <= {0}".format(frappe.db.escape(self.to_date)) if self.to_date else ""
|
||||
dr_or_cr = ("debit_in_account_currency" if erpnext.get_party_account_type(self.party_type) == 'Receivable'
|
||||
else "credit_in_account_currency")
|
||||
|
||||
|
@ -107,7 +107,7 @@ def get_item_groups(pos_profile):
|
||||
if pos_profile.get('item_groups'):
|
||||
# Get items based on the item groups defined in the POS profile
|
||||
for data in pos_profile.get('item_groups'):
|
||||
item_groups.extend(["'%s'" % frappe.db.escape(d.name) for d in get_child_nodes('Item Group', data.item_group)])
|
||||
item_groups.extend(["%s" % frappe.db.escape(d.name) for d in get_child_nodes('Item Group', data.item_group)])
|
||||
|
||||
return list(set(item_groups))
|
||||
|
||||
|
@ -255,10 +255,12 @@ def get_pricing_rules(args):
|
||||
|
||||
if parent_groups:
|
||||
if allow_blank: parent_groups.append('')
|
||||
condition = " ifnull("+field+", '') in ('" + \
|
||||
"', '".join([frappe.db.escape(d) for d in parent_groups])+"')"
|
||||
frappe.flags.tree_conditions[key] = condition
|
||||
condition = "ifnull({field}, '') in ({parent_groups})".format(
|
||||
field=field,
|
||||
parent_groups=", ".join([frappe.db.escape(d) for d in parent_groups])
|
||||
)
|
||||
|
||||
frappe.flags.tree_conditions[key] = condition
|
||||
return condition
|
||||
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -44,10 +45,12 @@
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -77,10 +80,12 @@
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -99,7 +104,7 @@
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "charge_type",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "\nActual\nOn Net Total\nOn Previous Row Amount\nOn Previous Row Total",
|
||||
"options": "\nActual\nOn Net Total\nOn Previous Row Amount\nOn Previous Row Total\nOn Item Quantity",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
@ -109,10 +114,12 @@
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -141,10 +148,12 @@
|
||||
"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,
|
||||
@ -172,10 +181,12 @@
|
||||
"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,
|
||||
@ -200,10 +211,12 @@
|
||||
"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,
|
||||
@ -232,10 +245,12 @@
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -265,10 +280,12 @@
|
||||
"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,
|
||||
@ -297,11 +314,13 @@
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0,
|
||||
"width": "300px"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -327,10 +346,12 @@
|
||||
"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,
|
||||
@ -358,10 +379,12 @@
|
||||
"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,
|
||||
@ -387,10 +410,12 @@
|
||||
"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,
|
||||
@ -419,10 +444,12 @@
|
||||
"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,
|
||||
@ -450,10 +477,12 @@
|
||||
"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,
|
||||
@ -482,10 +511,12 @@
|
||||
"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,
|
||||
@ -511,10 +542,12 @@
|
||||
"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,
|
||||
@ -542,10 +575,12 @@
|
||||
"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,
|
||||
@ -573,10 +608,12 @@
|
||||
"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,
|
||||
@ -604,10 +641,12 @@
|
||||
"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,
|
||||
@ -635,10 +674,12 @@
|
||||
"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,
|
||||
@ -666,6 +707,7 @@
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
@ -679,7 +721,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-12-06 13:37:44.483509",
|
||||
"modified": "2018-09-19 13:48:32.755198",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Taxes and Charges",
|
||||
@ -690,5 +732,6 @@
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
@ -375,7 +375,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
si.insert()
|
||||
|
||||
self.assertEqual(si.net_total, 4600)
|
||||
|
||||
|
||||
self.assertEqual(si.get("taxes")[0].tax_amount, 874.0)
|
||||
self.assertEqual(si.get("taxes")[0].total, 5474.0)
|
||||
|
||||
@ -405,12 +405,12 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
self.assertEqual(si.total, 975)
|
||||
self.assertEqual(si.net_total, 900)
|
||||
|
||||
|
||||
self.assertEqual(si.get("taxes")[0].tax_amount, 216.0)
|
||||
self.assertEqual(si.get("taxes")[0].total, 1116.0)
|
||||
|
||||
self.assertEqual(si.grand_total, 1116.0)
|
||||
|
||||
|
||||
def test_inclusive_rate_validations(self):
|
||||
si = frappe.copy_doc(test_records[2])
|
||||
for i, tax in enumerate(si.get("taxes")):
|
||||
@ -552,7 +552,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
self.assertEqual(si.grand_total, 1215.90)
|
||||
self.assertEqual(si.rounding_adjustment, 0.01)
|
||||
self.assertEqual(si.base_rounding_adjustment, 0.50)
|
||||
|
||||
|
||||
|
||||
def test_outstanding(self):
|
||||
w = self.make()
|
||||
@ -923,7 +923,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
self.assertRaises(SerialNoWarehouseError, si.submit)
|
||||
|
||||
def test_serial_numbers_against_delivery_note(self):
|
||||
"""
|
||||
"""
|
||||
check if the sales invoice item serial numbers and the delivery note items
|
||||
serial numbers are same
|
||||
"""
|
||||
@ -1238,7 +1238,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
def test_item_wise_tax_breakup_india(self):
|
||||
frappe.flags.country = "India"
|
||||
|
||||
|
||||
si = self.create_si_to_test_tax_breakup()
|
||||
itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(si)
|
||||
|
||||
@ -1256,12 +1256,12 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
self.assertEqual(itemised_tax, expected_itemised_tax)
|
||||
self.assertEqual(itemised_taxable_amount, expected_itemised_taxable_amount)
|
||||
|
||||
|
||||
frappe.flags.country = None
|
||||
|
||||
def test_item_wise_tax_breakup_outside_india(self):
|
||||
frappe.flags.country = "United States"
|
||||
|
||||
|
||||
si = self.create_si_to_test_tax_breakup()
|
||||
|
||||
itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(si)
|
||||
@ -1287,7 +1287,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
self.assertEqual(itemised_tax, expected_itemised_tax)
|
||||
self.assertEqual(itemised_taxable_amount, expected_itemised_taxable_amount)
|
||||
|
||||
|
||||
frappe.flags.country = None
|
||||
|
||||
def create_si_to_test_tax_breakup(self):
|
||||
@ -1375,7 +1375,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
shipping_rule = create_shipping_rule(shipping_rule_type = "Selling", shipping_rule_name = "Shipping Rule - Sales Invoice Test")
|
||||
|
||||
si = frappe.copy_doc(test_records[2])
|
||||
|
||||
|
||||
si.shipping_rule = shipping_rule.name
|
||||
si.insert()
|
||||
|
||||
@ -1392,14 +1392,14 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
"cost_center": shipping_rule.cost_center,
|
||||
"tax_amount": shipping_amount,
|
||||
"description": shipping_rule.name
|
||||
}
|
||||
}
|
||||
si.append("taxes", shipping_charge)
|
||||
si.save()
|
||||
|
||||
self.assertEqual(si.net_total, 1250)
|
||||
|
||||
self.assertEqual(si.total_taxes_and_charges, 577.05)
|
||||
self.assertEqual(si.grand_total, 1827.05)
|
||||
self.assertEqual(si.grand_total, 1827.05)
|
||||
|
||||
def test_create_invoice_without_terms(self):
|
||||
si = create_sales_invoice(do_not_save=1)
|
||||
@ -1496,7 +1496,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
for gle in gl_entries:
|
||||
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
|
||||
|
||||
|
||||
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
|
||||
accounts_settings.save()
|
||||
|
||||
@ -1524,9 +1524,9 @@ def create_sales_invoice(**args):
|
||||
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
||||
"qty": args.qty or 1,
|
||||
"rate": args.rate or 100,
|
||||
"income_account": "Sales - _TC",
|
||||
"expense_account": "Cost of Goods Sold - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"income_account": args.income_account or "Sales - _TC",
|
||||
"expense_account": args.expense_account or "Cost of Goods Sold - _TC",
|
||||
"cost_center": args.cost_center or "_Test Cost Center - _TC",
|
||||
"serial_no": args.serial_no
|
||||
})
|
||||
|
||||
|
@ -33,7 +33,7 @@
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "charge_type",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "\nActual\nOn Net Total\nOn Previous Row Amount\nOn Previous Row Total",
|
||||
"options": "\nActual\nOn Net Total\nOn Previous Row Amount\nOn Previous Row Total\nOn Item Quantity",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
@ -652,7 +652,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-08-21 16:15:51.518582",
|
||||
"modified": "2018-09-19 13:48:59.341454",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Taxes and Charges",
|
||||
@ -666,4 +666,4 @@
|
||||
"track_changes": 0,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ class TaxRule(Document):
|
||||
for d in filters:
|
||||
if conds:
|
||||
conds += " and "
|
||||
conds += """ifnull({0}, '') = '{1}'""".format(d, frappe.db.escape(cstr(filters[d])))
|
||||
conds += """ifnull({0}, '') = {1}""".format(d, frappe.db.escape(cstr(filters[d])))
|
||||
|
||||
if self.from_date and self.to_date:
|
||||
conds += """ and ((from_date > '{from_date}' and from_date < '{to_date}') or
|
||||
@ -152,7 +152,7 @@ def get_tax_template(posting_date, args):
|
||||
customer_group_condition = get_customer_group_condition(value)
|
||||
conditions.append("ifnull({0}, '') in ('', {1})".format(key, customer_group_condition))
|
||||
else:
|
||||
conditions.append("ifnull({0}, '') in ('', '{1}')".format(key, frappe.db.escape(cstr(value))))
|
||||
conditions.append("ifnull({0}, '') in ('', {1})".format(key, frappe.db.escape(cstr(value))))
|
||||
|
||||
tax_rule = frappe.db.sql("""select * from `tabTax Rule`
|
||||
where {0}""".format(" and ".join(conditions)), as_dict = True)
|
||||
@ -180,7 +180,7 @@ def get_tax_template(posting_date, args):
|
||||
|
||||
def get_customer_group_condition(customer_group):
|
||||
condition = ""
|
||||
customer_groups = ["'%s'"%(frappe.db.escape(d.name)) for d in get_parent_customer_groups(customer_group)]
|
||||
customer_groups = ["%s"%(frappe.db.escape(d.name)) for d in get_parent_customer_groups(customer_group)]
|
||||
if customer_groups:
|
||||
condition = ",".join(['%s'] * len(customer_groups))%(tuple(customer_groups))
|
||||
return condition
|
@ -443,7 +443,7 @@ def get_timeline_data(doctype, name):
|
||||
# fetch and append data from Activity Log
|
||||
data += frappe.db.sql("""select {fields}
|
||||
from `tabActivity Log`
|
||||
where reference_doctype="{doctype}" and reference_name="{name}"
|
||||
where reference_doctype={doctype} and reference_name={name}
|
||||
and status!='Success' and creation > {after}
|
||||
{group_by} order by creation desc
|
||||
""".format(doctype=frappe.db.escape(doctype), name=frappe.db.escape(name), fields=fields,
|
||||
|
@ -107,6 +107,11 @@ frappe.query_reports["Accounts Receivable"] = {
|
||||
"label": __("Show PDC in Print"),
|
||||
"fieldtype": "Check",
|
||||
},
|
||||
{
|
||||
"fieldname":"based_on_payment_terms",
|
||||
"label": __("Based On Payment Terms"),
|
||||
"fieldtype": "Check",
|
||||
},
|
||||
{
|
||||
"fieldname":"tax_id",
|
||||
"label": __("Tax Id"),
|
||||
|
@ -4,7 +4,7 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe, erpnext
|
||||
from frappe import _, scrub
|
||||
from frappe.utils import getdate, nowdate, flt, cint
|
||||
from frappe.utils import getdate, nowdate, flt, cint, formatdate, cstr
|
||||
|
||||
class ReceivablePayableReport(object):
|
||||
def __init__(self, filters=None):
|
||||
@ -57,6 +57,21 @@ class ReceivablePayableReport(object):
|
||||
|
||||
credit_or_debit_note = "Credit Note" if args.get("party_type") == "Customer" else "Debit Note"
|
||||
|
||||
if self.filters.based_on_payment_terms:
|
||||
columns.append({
|
||||
"label": "Payment Term",
|
||||
"fieldname": "payment_term",
|
||||
"fieldtype": "Data",
|
||||
"width": 120
|
||||
})
|
||||
columns.append({
|
||||
"label": "Invoice Grand Total",
|
||||
"fieldname": "invoice_grand_total",
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 120
|
||||
})
|
||||
|
||||
for label in ("Invoiced Amount", "Paid Amount", credit_or_debit_note, "Outstanding Amount"):
|
||||
columns.append({
|
||||
"label": label,
|
||||
@ -97,12 +112,6 @@ class ReceivablePayableReport(object):
|
||||
"options": "Currency",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"fieldname": "pdc/lc_date",
|
||||
"label": _("PDC/LC Date"),
|
||||
"fieldtype": "Date",
|
||||
"width": 110
|
||||
},
|
||||
{
|
||||
"fieldname": "pdc/lc_ref",
|
||||
"label": _("PDC/LC Ref"),
|
||||
@ -113,14 +122,14 @@ class ReceivablePayableReport(object):
|
||||
"fieldname": "pdc/lc_amount",
|
||||
"label": _("PDC/LC Amount"),
|
||||
"fieldtype": "Currency",
|
||||
"options": "Currency",
|
||||
"options": "currency",
|
||||
"width": 130
|
||||
},
|
||||
{
|
||||
"fieldname": "remaining_balance",
|
||||
"label": _("Remaining Balance"),
|
||||
"fieldtype": "Currency",
|
||||
"options": "Currency",
|
||||
"options": "currency",
|
||||
"width": 130
|
||||
}]
|
||||
|
||||
@ -151,108 +160,203 @@ class ReceivablePayableReport(object):
|
||||
|
||||
def get_data(self, party_naming_by, args):
|
||||
from erpnext.accounts.utils import get_currency_precision
|
||||
currency_precision = get_currency_precision() or 2
|
||||
dr_or_cr = "debit" if args.get("party_type") == "Customer" else "credit"
|
||||
self.currency_precision = get_currency_precision() or 2
|
||||
self.dr_or_cr = "debit" if args.get("party_type") == "Customer" else "credit"
|
||||
|
||||
future_vouchers = self.get_entries_after(self.filters.report_date, args.get("party_type"))
|
||||
|
||||
if not self.filters.get("company"):
|
||||
self.filters["company"] = frappe.db.get_single_value('Global Defaults', 'default_company')
|
||||
|
||||
company_currency = frappe.get_cached_value('Company', self.filters.get("company"), "default_currency")
|
||||
self.company_currency = frappe.get_cached_value('Company', self.filters.get("company"), "default_currency")
|
||||
|
||||
return_entries = self.get_return_entries(args.get("party_type"))
|
||||
|
||||
data = []
|
||||
pdc_details = get_pdc_details(args.get("party_type"), self.filters.report_date)
|
||||
self.pdc_details = get_pdc_details(args.get("party_type"), self.filters.report_date)
|
||||
gl_entries_data = self.get_entries_till(self.filters.report_date, args.get("party_type"))
|
||||
|
||||
if gl_entries_data:
|
||||
voucher_nos = [d.voucher_no for d in gl_entries_data] or []
|
||||
dn_details = get_dn_details(args.get("party_type"), voucher_nos)
|
||||
voucher_details = get_voucher_details(args.get("party_type"), voucher_nos, dn_details)
|
||||
self.voucher_details = get_voucher_details(args.get("party_type"), voucher_nos, dn_details)
|
||||
|
||||
if self.filters.based_on_payment_terms:
|
||||
self.payment_term_map = self.get_payment_term_detail(voucher_nos)
|
||||
|
||||
for gle in gl_entries_data:
|
||||
if self.is_receivable_or_payable(gle, dr_or_cr, future_vouchers):
|
||||
outstanding_amount, credit_note_amount = self.get_outstanding_amount(gle,
|
||||
self.filters.report_date, dr_or_cr, return_entries, currency_precision)
|
||||
if abs(outstanding_amount) > 0.1/10**currency_precision:
|
||||
row = [gle.posting_date, gle.party]
|
||||
if self.is_receivable_or_payable(gle, self.dr_or_cr, future_vouchers):
|
||||
outstanding_amount, credit_note_amount, payment_amount = self.get_outstanding_amount(
|
||||
gle,self.filters.report_date, self.dr_or_cr, return_entries)
|
||||
|
||||
# customer / supplier name
|
||||
if party_naming_by == "Naming Series":
|
||||
row += [self.get_party_name(gle.party_type, gle.party)]
|
||||
temp_outstanding_amt = outstanding_amount
|
||||
temp_credit_note_amt = credit_note_amount
|
||||
|
||||
# get due date
|
||||
due_date = voucher_details.get(gle.voucher_no, {}).get("due_date", "")
|
||||
bill_date = voucher_details.get(gle.voucher_no, {}).get("bill_date", "")
|
||||
if abs(outstanding_amount) > 0.1/10**self.currency_precision:
|
||||
if self.filters.based_on_payment_terms and self.payment_term_map.get(gle.voucher_no):
|
||||
for d in self.payment_term_map.get(gle.voucher_no):
|
||||
# Allocate payment amount based on payment terms(FIFO order)
|
||||
payment_amount, d.payment_amount = self.allocate_based_on_fifo(payment_amount, d.payment_term_amount)
|
||||
|
||||
row += [gle.voucher_type, gle.voucher_no, due_date]
|
||||
term_outstanding_amount = d.payment_term_amount - d.payment_amount
|
||||
|
||||
# get supplier bill details
|
||||
if args.get("party_type") == "Supplier":
|
||||
row += [
|
||||
voucher_details.get(gle.voucher_no, {}).get("bill_no", ""),
|
||||
voucher_details.get(gle.voucher_no, {}).get("bill_date", "")
|
||||
]
|
||||
# Allocate credit note based on payment terms(FIFO order)
|
||||
credit_note_amount, d.credit_note_amount = self.allocate_based_on_fifo(credit_note_amount, term_outstanding_amount)
|
||||
|
||||
# invoiced and paid amounts
|
||||
invoiced_amount = gle.get(dr_or_cr) if (gle.get(dr_or_cr) > 0) else 0
|
||||
paid_amt = invoiced_amount - outstanding_amount - credit_note_amount
|
||||
row += [invoiced_amount, paid_amt, credit_note_amount, outstanding_amount]
|
||||
term_outstanding_amount -= d.credit_note_amount
|
||||
|
||||
row_outstanding = term_outstanding_amount
|
||||
# Allocate PDC based on payment terms(FIFO order)
|
||||
d.pdc_details, d.pdc_amount = self.allocate_pdc_amount_in_fifo(gle, row_outstanding)
|
||||
|
||||
if term_outstanding_amount > 0:
|
||||
row = self.prepare_row(party_naming_by, args, gle, term_outstanding_amount,
|
||||
d.credit_note_amount, d.due_date, d.payment_amount , d.payment_term_amount,
|
||||
d.description, d.pdc_amount, d.pdc_details)
|
||||
data.append(row)
|
||||
|
||||
if credit_note_amount:
|
||||
row = self.prepare_row_without_payment_terms(party_naming_by, args, gle, temp_outstanding_amt,
|
||||
temp_credit_note_amt)
|
||||
data.append(row)
|
||||
|
||||
# ageing data
|
||||
if self.filters.ageing_based_on == "Due Date":
|
||||
entry_date = due_date
|
||||
elif self.filters.ageing_based_on == "Supplier Invoice Date":
|
||||
entry_date = bill_date
|
||||
else:
|
||||
entry_date = gle.posting_date
|
||||
|
||||
row += get_ageing_data(cint(self.filters.range1), cint(self.filters.range2),
|
||||
cint(self.filters.range3), self.age_as_on, entry_date, outstanding_amount)
|
||||
|
||||
|
||||
# issue 6371-Ageing buckets should not have amounts if due date is not reached
|
||||
if self.filters.ageing_based_on == "Due Date" \
|
||||
and getdate(due_date) > getdate(self.filters.report_date):
|
||||
row[-1]=row[-2]=row[-3]=row[-4]=0
|
||||
|
||||
if self.filters.ageing_based_on == "Supplier Invoice Date" \
|
||||
and getdate(bill_date) > getdate(self.filters.report_date):
|
||||
|
||||
row[-1]=row[-2]=row[-3]=row[-4]=0
|
||||
|
||||
if self.filters.get(scrub(args.get("party_type"))):
|
||||
row.append(gle.account_currency)
|
||||
else:
|
||||
row.append(company_currency)
|
||||
|
||||
pdc = pdc_details.get((gle.voucher_no, gle.party), {})
|
||||
|
||||
remaining_balance = outstanding_amount - flt(pdc.get("pdc_amount"))
|
||||
row += [pdc.get("pdc_date"), pdc.get("pdc_ref"),
|
||||
flt(pdc.get("pdc_amount")), remaining_balance]
|
||||
|
||||
if args.get('party_type') == 'Customer':
|
||||
# customer LPO
|
||||
row += [voucher_details.get(gle.voucher_no, {}).get("po_no")]
|
||||
|
||||
# Delivery Note
|
||||
row += [voucher_details.get(gle.voucher_no, {}).get("delivery_note")]
|
||||
|
||||
# customer territory / supplier group
|
||||
if args.get("party_type") == "Customer":
|
||||
row += [self.get_territory(gle.party), self.get_customer_group(gle.party),
|
||||
voucher_details.get(gle.voucher_no, {}).get("sales_person")]
|
||||
if args.get("party_type") == "Supplier":
|
||||
row += [self.get_supplier_group(gle.party)]
|
||||
|
||||
row.append(gle.remarks)
|
||||
data.append(row)
|
||||
|
||||
row = self.prepare_row_without_payment_terms(party_naming_by, args, gle, outstanding_amount,
|
||||
credit_note_amount)
|
||||
data.append(row)
|
||||
return data
|
||||
|
||||
def allocate_pdc_amount_in_fifo(self, gle, row_outstanding):
|
||||
pdc_list = self.pdc_details.get((gle.voucher_no, gle.party), [])
|
||||
|
||||
pdc_details = []
|
||||
pdc_amount = 0
|
||||
for pdc in pdc_list:
|
||||
if row_outstanding <= pdc.pdc_amount:
|
||||
pdc_amount += row_outstanding
|
||||
pdc.pdc_amount -= row_outstanding
|
||||
if row_outstanding and pdc.pdc_ref and pdc.pdc_date:
|
||||
pdc_details.append(cstr(pdc.pdc_ref) + "/" + formatdate(pdc.pdc_date))
|
||||
row_outstanding = 0
|
||||
|
||||
else:
|
||||
pdc_amount = pdc.pdc_amount
|
||||
if pdc.pdc_amount and pdc.pdc_ref and pdc.pdc_date:
|
||||
pdc_details.append(cstr(pdc.pdc_ref) + "/" + formatdate(pdc.pdc_date))
|
||||
pdc.pdc_amount = 0
|
||||
row_outstanding -= pdc_amount
|
||||
|
||||
return pdc_details, pdc_amount
|
||||
|
||||
def prepare_row_without_payment_terms(self, party_naming_by, args, gle, outstanding_amount, credit_note_amount):
|
||||
pdc_list = self.pdc_details.get((gle.voucher_no, gle.party), [])
|
||||
pdc_amount = 0
|
||||
pdc_details = []
|
||||
for d in pdc_list:
|
||||
pdc_amount += flt(d.pdc_amount)
|
||||
if pdc_amount and d.pdc_ref and d.pdc_date:
|
||||
pdc_details.append(cstr(d.pdc_ref) + "/" + formatdate(d.pdc_date))
|
||||
|
||||
row = self.prepare_row(party_naming_by, args, gle, outstanding_amount,
|
||||
credit_note_amount, pdc_amount=pdc_amount, pdc_details=pdc_details)
|
||||
|
||||
return row
|
||||
|
||||
|
||||
def allocate_based_on_fifo(self, total_amount, row_amount):
|
||||
allocated_amount = 0
|
||||
if row_amount <= total_amount:
|
||||
allocated_amount = row_amount
|
||||
total_amount -= row_amount
|
||||
else:
|
||||
allocated_amount = total_amount
|
||||
total_amount = 0
|
||||
|
||||
return total_amount, allocated_amount
|
||||
|
||||
def prepare_row(self, party_naming_by, args, gle, outstanding_amount, credit_note_amount,
|
||||
due_date=None, paid_amt=None, payment_term_amount=None, payment_term=None, pdc_amount=None, pdc_details=None):
|
||||
row = [gle.posting_date, gle.party]
|
||||
|
||||
# customer / supplier name
|
||||
if party_naming_by == "Naming Series":
|
||||
row += [self.get_party_name(gle.party_type, gle.party)]
|
||||
|
||||
# get due date
|
||||
if not due_date:
|
||||
due_date = self.voucher_details.get(gle.voucher_no, {}).get("due_date", "")
|
||||
bill_date = self.voucher_details.get(gle.voucher_no, {}).get("bill_date", "")
|
||||
|
||||
row += [gle.voucher_type, gle.voucher_no, due_date]
|
||||
|
||||
# get supplier bill details
|
||||
if args.get("party_type") == "Supplier":
|
||||
row += [
|
||||
self.voucher_details.get(gle.voucher_no, {}).get("bill_no", ""),
|
||||
self.voucher_details.get(gle.voucher_no, {}).get("bill_date", "")
|
||||
]
|
||||
|
||||
# invoiced and paid amounts
|
||||
invoiced_amount = gle.get(self.dr_or_cr) if (gle.get(self.dr_or_cr) > 0) else 0
|
||||
|
||||
if self.filters.based_on_payment_terms:
|
||||
row+=[payment_term, invoiced_amount]
|
||||
if payment_term_amount:
|
||||
invoiced_amount = payment_term_amount
|
||||
|
||||
if not payment_term_amount:
|
||||
paid_amt = invoiced_amount - outstanding_amount - credit_note_amount
|
||||
row += [invoiced_amount, paid_amt, credit_note_amount, outstanding_amount]
|
||||
|
||||
# ageing data
|
||||
if self.filters.ageing_based_on == "Due Date":
|
||||
entry_date = due_date
|
||||
elif self.filters.ageing_based_on == "Supplier Invoice Date":
|
||||
entry_date = bill_date
|
||||
else:
|
||||
entry_date = gle.posting_date
|
||||
|
||||
row += get_ageing_data(cint(self.filters.range1), cint(self.filters.range2),
|
||||
cint(self.filters.range3), self.age_as_on, entry_date, outstanding_amount)
|
||||
|
||||
|
||||
# issue 6371-Ageing buckets should not have amounts if due date is not reached
|
||||
if self.filters.ageing_based_on == "Due Date" \
|
||||
and getdate(due_date) > getdate(self.filters.report_date):
|
||||
row[-1]=row[-2]=row[-3]=row[-4]=0
|
||||
|
||||
if self.filters.ageing_based_on == "Supplier Invoice Date" \
|
||||
and getdate(bill_date) > getdate(self.filters.report_date):
|
||||
|
||||
row[-1]=row[-2]=row[-3]=row[-4]=0
|
||||
|
||||
if self.filters.get(scrub(args.get("party_type"))):
|
||||
row.append(gle.account_currency)
|
||||
else:
|
||||
row.append(self.company_currency)
|
||||
|
||||
remaining_balance = outstanding_amount - flt(pdc_amount)
|
||||
pdc_details = ", ".join(pdc_details)
|
||||
row += [pdc_details, pdc_amount, remaining_balance]
|
||||
|
||||
if args.get('party_type') == 'Customer':
|
||||
# customer LPO
|
||||
row += [self.voucher_details.get(gle.voucher_no, {}).get("po_no")]
|
||||
|
||||
# Delivery Note
|
||||
row += [self.voucher_details.get(gle.voucher_no, {}).get("delivery_note")]
|
||||
|
||||
# customer territory / supplier group
|
||||
if args.get("party_type") == "Customer":
|
||||
row += [self.get_territory(gle.party), self.get_customer_group(gle.party),
|
||||
self.voucher_details.get(gle.voucher_no, {}).get("sales_person")]
|
||||
if args.get("party_type") == "Supplier":
|
||||
row += [self.get_supplier_group(gle.party)]
|
||||
|
||||
row.append(gle.remarks)
|
||||
|
||||
return row
|
||||
|
||||
def get_entries_after(self, report_date, party_type):
|
||||
# returns a distinct list
|
||||
return list(set([(e.voucher_type, e.voucher_no) for e in self.get_gl_entries(party_type, report_date, for_future=True)]))
|
||||
@ -280,25 +384,25 @@ class ReceivablePayableReport(object):
|
||||
doctype = "Sales Invoice" if party_type=="Customer" else "Purchase Invoice"
|
||||
return [d.name for d in frappe.get_all(doctype, filters={"is_return": 1, "docstatus": 1})]
|
||||
|
||||
def get_outstanding_amount(self, gle, report_date, dr_or_cr, return_entries, currency_precision):
|
||||
def get_outstanding_amount(self, gle, report_date, dr_or_cr, return_entries):
|
||||
payment_amount, credit_note_amount = 0.0, 0.0
|
||||
reverse_dr_or_cr = "credit" if dr_or_cr=="debit" else "debit"
|
||||
|
||||
for e in self.get_gl_entries_for(gle.party, gle.party_type, gle.voucher_type, gle.voucher_no):
|
||||
if getdate(e.posting_date) <= report_date and e.name!=gle.name:
|
||||
amount = flt(e.get(reverse_dr_or_cr), currency_precision) - flt(e.get(dr_or_cr), currency_precision)
|
||||
amount = flt(e.get(reverse_dr_or_cr), self.currency_precision) - flt(e.get(dr_or_cr), self.currency_precision)
|
||||
if e.voucher_no not in return_entries:
|
||||
payment_amount += amount
|
||||
else:
|
||||
credit_note_amount += amount
|
||||
|
||||
outstanding_amount = (flt((flt(gle.get(dr_or_cr), currency_precision)
|
||||
- flt(gle.get(reverse_dr_or_cr), currency_precision)
|
||||
- payment_amount - credit_note_amount), currency_precision))
|
||||
outstanding_amount = (flt((flt(gle.get(dr_or_cr), self.currency_precision)
|
||||
- flt(gle.get(reverse_dr_or_cr), self.currency_precision)
|
||||
- payment_amount - credit_note_amount), self.currency_precision))
|
||||
|
||||
credit_note_amount = flt(credit_note_amount, currency_precision)
|
||||
credit_note_amount = flt(credit_note_amount, self.currency_precision)
|
||||
|
||||
return outstanding_amount, credit_note_amount
|
||||
return outstanding_amount, credit_note_amount, payment_amount
|
||||
|
||||
def get_party_name(self, party_type, party_name):
|
||||
return self.get_party_map(party_type).get(party_name, {}).get("customer_name" if party_type == "Customer" else "supplier_name") or ""
|
||||
@ -383,7 +487,7 @@ class ReceivablePayableReport(object):
|
||||
conditions.append("""party in (select name from tabCustomer
|
||||
where exists(select name from `tabCustomer Group` where lft >= {0} and rgt <= {1}
|
||||
and name=tabCustomer.customer_group))""".format(lft, rgt))
|
||||
|
||||
|
||||
if self.filters.get("territory"):
|
||||
lft, rgt = frappe.db.get_value("Territory",
|
||||
self.filters.get("territory"), ["lft", "rgt"])
|
||||
@ -415,7 +519,7 @@ class ReceivablePayableReport(object):
|
||||
conditions.append("""party in (select name from tabSupplier
|
||||
where supplier_group=%s)""")
|
||||
values.append(self.filters.get("supplier_group"))
|
||||
|
||||
|
||||
return " and ".join(conditions), values
|
||||
|
||||
def get_gl_entries_for(self, party, party_type, against_voucher_type, against_voucher):
|
||||
@ -432,6 +536,31 @@ class ReceivablePayableReport(object):
|
||||
.get(against_voucher_type, {})\
|
||||
.get(against_voucher, [])
|
||||
|
||||
def get_payment_term_detail(self, voucher_nos):
|
||||
payment_term_map = frappe._dict()
|
||||
payment_terms_details = frappe.db.sql(""" select si.name,
|
||||
party_account_currency, currency, si.conversion_rate,
|
||||
ps.due_date, ps.payment_amount, ps.description
|
||||
from `tabSales Invoice` si, `tabPayment Schedule` ps
|
||||
where si.name = ps.parent and
|
||||
si.docstatus = 1 and si.company = %s and
|
||||
si.name in (%s) order by ps.due_date
|
||||
""" % (frappe.db.escape(self.filters.company), ','.join(['%s'] *len(voucher_nos))),
|
||||
(tuple(voucher_nos)), as_dict = 1)
|
||||
|
||||
for d in payment_terms_details:
|
||||
if self.filters.get("customer") and d.currency == d.party_account_currency:
|
||||
payment_term_amount = d.payment_amount
|
||||
else:
|
||||
payment_term_amount = flt(flt(d.payment_amount) * flt(d.conversion_rate), self.currency_precision)
|
||||
|
||||
payment_term_map.setdefault(d.name, []).append(frappe._dict({
|
||||
"due_date": d.due_date,
|
||||
"payment_term_amount": payment_term_amount,
|
||||
"description": d.description
|
||||
}))
|
||||
return payment_term_map
|
||||
|
||||
def get_chart_data(self, columns, data):
|
||||
ageing_columns = columns[self.ageing_col_idx_start : self.ageing_col_idx_start+4]
|
||||
|
||||
@ -479,12 +608,11 @@ def get_ageing_data(first_range, second_range, third_range, age_as_on, entry_dat
|
||||
|
||||
def get_pdc_details(party_type, report_date):
|
||||
pdc_details = frappe._dict()
|
||||
|
||||
for pdc in frappe.db.sql("""
|
||||
pdc_via_pe = frappe.db.sql("""
|
||||
select
|
||||
pref.reference_name as invoice_no, pent.party, pent.party_type,
|
||||
max(pent.posting_date) as pdc_date, sum(ifnull(pref.allocated_amount,0)) as pdc_amount,
|
||||
GROUP_CONCAT(pent.reference_no SEPARATOR ', ') as pdc_ref
|
||||
pent.posting_date as pdc_date, ifnull(pref.allocated_amount,0) as pdc_amount,
|
||||
pent.reference_no as pdc_ref
|
||||
from
|
||||
`tabPayment Entry` as pent inner join `tabPayment Entry Reference` as pref
|
||||
on
|
||||
@ -492,19 +620,22 @@ def get_pdc_details(party_type, report_date):
|
||||
where
|
||||
pent.docstatus < 2 and pent.posting_date > %s
|
||||
and pent.party_type = %s
|
||||
group by pent.party, pref.reference_name""", (report_date, party_type), as_dict=1):
|
||||
pdc_details.setdefault((pdc.invoice_no, pdc.party), pdc)
|
||||
""", (report_date, party_type), as_dict=1)
|
||||
|
||||
for pdc in pdc_via_pe:
|
||||
pdc_details.setdefault((pdc.invoice_no, pdc.party), []).append(pdc)
|
||||
|
||||
if scrub(party_type):
|
||||
amount_field = ("jea.debit_in_account_currency"
|
||||
if party_type == 'Supplier' else "jea.credit_in_account_currency")
|
||||
else:
|
||||
amount_field = "jea.debit + jea.credit"
|
||||
|
||||
for pdc in frappe.db.sql("""
|
||||
pdc_via_je = frappe.db.sql("""
|
||||
select
|
||||
jea.reference_name as invoice_no, jea.party, jea.party_type,
|
||||
max(je.posting_date) as pdc_date, sum(ifnull({0},0)) as pdc_amount,
|
||||
GROUP_CONCAT(je.cheque_no SEPARATOR ', ') as pdc_ref
|
||||
je.posting_date as pdc_date, ifnull({0},0) as pdc_amount,
|
||||
je.cheque_no as pdc_ref
|
||||
from
|
||||
`tabJournal Entry` as je inner join `tabJournal Entry Account` as jea
|
||||
on
|
||||
@ -512,16 +643,10 @@ def get_pdc_details(party_type, report_date):
|
||||
where
|
||||
je.docstatus < 2 and je.posting_date > %s
|
||||
and jea.party_type = %s
|
||||
group by jea.party, jea.reference_name""".format(amount_field), (report_date, party_type), as_dict=1):
|
||||
if (pdc.invoice_no, pdc.party) in pdc_details:
|
||||
key = (pdc.invoice_no, pdc.party)
|
||||
pdc_details[key]["pdc_amount"] += pdc.pdc_amount
|
||||
if pdc.pdc_ref:
|
||||
pdc_details[key]["pdc_ref"] += ", " + pdc.pdc_ref
|
||||
if pdc.pdc_date:
|
||||
pdc_details[key]["pdc_date"] = max(pdc_details[key]["pdc_date"], pdc.pdc_date)
|
||||
else:
|
||||
pdc_details.setdefault((pdc.invoice_no, pdc.party), pdc)
|
||||
""".format(amount_field), (report_date, party_type), as_dict=1)
|
||||
|
||||
for pdc in pdc_via_je:
|
||||
pdc_details.setdefault((pdc.invoice_no, pdc.party), []).append(pdc)
|
||||
|
||||
return pdc_details
|
||||
|
||||
|
@ -0,0 +1,84 @@
|
||||
import frappe
|
||||
import frappe.defaults
|
||||
import unittest
|
||||
from frappe.utils import today, getdate, add_days
|
||||
from erpnext.accounts.report.accounts_receivable.accounts_receivable import execute
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
||||
|
||||
class TestAccountsReceivable(unittest.TestCase):
|
||||
def test_accounts_receivable(self):
|
||||
frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company 2'")
|
||||
frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 2'")
|
||||
|
||||
filters = {
|
||||
'company': '_Test Company 2',
|
||||
'based_on_payment_terms': 1
|
||||
}
|
||||
|
||||
name = make_sales_invoice()
|
||||
report = execute(filters)
|
||||
|
||||
expected_data = [[100,30], [100,50], [100,20]]
|
||||
|
||||
self.assertEqual(expected_data[0], report[1][0][6:8])
|
||||
self.assertEqual(expected_data[1], report[1][1][6:8])
|
||||
self.assertEqual(expected_data[2], report[1][2][6:8])
|
||||
|
||||
make_payment(name)
|
||||
report = execute(filters)
|
||||
|
||||
expected_data_after_payment = [[100,50], [100,20]]
|
||||
|
||||
self.assertEqual(expected_data_after_payment[0], report[1][0][6:8])
|
||||
self.assertEqual(expected_data_after_payment[1], report[1][1][6:8])
|
||||
|
||||
make_credit_note(name)
|
||||
report = execute(filters)
|
||||
|
||||
expected_data_after_credit_note = [[100,100,30,100,-30]]
|
||||
|
||||
self.assertEqual(expected_data_after_credit_note[0], report[1][0][6:11])
|
||||
|
||||
|
||||
def make_sales_invoice():
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
si = create_sales_invoice(company="_Test Company 2",
|
||||
customer = '_Test Customer 2',
|
||||
currency = 'EUR',
|
||||
warehouse = 'Finished Goods - _TC2',
|
||||
debit_to = 'Debtors - _TC2',
|
||||
income_account = 'Sales - _TC2',
|
||||
expense_account = 'Cost of Goods Sold - _TC2',
|
||||
cost_center = '_Test Company 2 - _TC2',
|
||||
do_not_save=1)
|
||||
|
||||
si.append('payment_schedule', dict(due_date=getdate(add_days(today(), 30)), invoice_portion=30.00, payment_amount=30))
|
||||
si.append('payment_schedule', dict(due_date=getdate(add_days(today(), 60)), invoice_portion=50.00, payment_amount=50))
|
||||
si.append('payment_schedule', dict(due_date=getdate(add_days(today(), 90)), invoice_portion=20.00, payment_amount=20))
|
||||
|
||||
si.submit()
|
||||
|
||||
return si.name
|
||||
|
||||
def make_payment(docname):
|
||||
pe = get_payment_entry("Sales Invoice", docname, bank_account="Cash - _TC2", party_amount=30)
|
||||
pe.paid_from = "Debtors - _TC2"
|
||||
pe.insert()
|
||||
pe.submit()
|
||||
|
||||
|
||||
def make_credit_note(docname):
|
||||
create_sales_invoice(company="_Test Company 2",
|
||||
customer = '_Test Customer 2',
|
||||
currency = 'EUR',
|
||||
qty = -1,
|
||||
warehouse = 'Finished Goods - _TC2',
|
||||
debit_to = 'Debtors - _TC2',
|
||||
income_account = 'Sales - _TC2',
|
||||
expense_account = 'Cost of Goods Sold - _TC2',
|
||||
cost_center = '_Test Company 2 - _TC2',
|
||||
is_return = 1,
|
||||
return_against = docname)
|
||||
|
@ -86,20 +86,20 @@ def get_columns(filters):
|
||||
_("Total Variance") + ":Float:80"]
|
||||
else:
|
||||
return columns
|
||||
|
||||
|
||||
def get_cost_centers(filters):
|
||||
cond = "and 1=1"
|
||||
if filters.get("budget_against") == "Cost Center":
|
||||
cond = "order by lft"
|
||||
|
||||
return frappe.db.sql_list("""select name from `tab{tab}` where company=%s
|
||||
return frappe.db.sql_list("""select name from `tab{tab}` where company=%s
|
||||
{cond}""".format(tab=filters.get("budget_against"), cond=cond), filters.get("company"))
|
||||
|
||||
#Get cost center & target details
|
||||
def get_cost_center_target_details(filters):
|
||||
cond = ""
|
||||
if filters.get("cost_center"):
|
||||
cond += " and b.cost_center='%s'" % frappe.db.escape(filters.get("cost_center"))
|
||||
cond += " and b.cost_center=%s" % frappe.db.escape(filters.get("cost_center"))
|
||||
|
||||
return frappe.db.sql("""
|
||||
select b.{budget_against} as budget_against, b.monthly_distribution, ba.account, ba.budget_amount,b.fiscal_year
|
||||
@ -159,7 +159,7 @@ def get_cost_center_account_month_map(filters):
|
||||
|
||||
for ccd in cost_center_target_details:
|
||||
actual_details = get_actual_details(ccd.budget_against, filters)
|
||||
|
||||
|
||||
for month_id in range(1, 13):
|
||||
month = datetime.date(2013, month_id, 1).strftime('%B')
|
||||
cam_map.setdefault(ccd.budget_against, {}).setdefault(ccd.account, {}).setdefault(ccd.fiscal_year,{})\
|
||||
@ -172,7 +172,7 @@ def get_cost_center_account_month_map(filters):
|
||||
if ccd.monthly_distribution else 100.0/12
|
||||
|
||||
tav_dict.target = flt(ccd.budget_amount) * month_percentage / 100
|
||||
|
||||
|
||||
for ad in actual_details.get(ccd.account, []):
|
||||
if ad.month_name == month:
|
||||
tav_dict.actual += flt(ad.debit) - flt(ad.credit)
|
||||
|
@ -101,7 +101,7 @@ def get_income_expense_data(companies, fiscal_year, filters):
|
||||
net_profit_loss = get_net_profit_loss(income, expense, companies, filters.company, company_currency, True)
|
||||
|
||||
return income, expense, net_profit_loss
|
||||
|
||||
|
||||
def get_cash_flow_data(fiscal_year, companies, filters):
|
||||
cash_flow_accounts = get_cash_flow_accounts()
|
||||
|
||||
@ -123,7 +123,7 @@ def get_cash_flow_data(fiscal_year, companies, filters):
|
||||
# add first net income in operations section
|
||||
if net_profit_loss:
|
||||
net_profit_loss.update({
|
||||
"indent": 1,
|
||||
"indent": 1,
|
||||
"parent_account": cash_flow_accounts[0]['section_header']
|
||||
})
|
||||
data.append(net_profit_loss)
|
||||
@ -274,7 +274,8 @@ def get_companies(filters):
|
||||
return all_companies, companies
|
||||
|
||||
def get_subsidiary_companies(company):
|
||||
lft, rgt = frappe.db.get_value('Company', company, ["lft", "rgt"])
|
||||
lft, rgt = frappe.get_cached_value('Company',
|
||||
company, ["lft", "rgt"])
|
||||
|
||||
return frappe.db.sql_list("""select name from `tabCompany`
|
||||
where lft >= {0} and rgt <= {1} order by lft, rgt""".format(lft, rgt))
|
||||
@ -327,7 +328,7 @@ def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, g
|
||||
accounts_by_name, ignore_closing_entries=False):
|
||||
"""Returns a dict like { "account": [gl entries], ... }"""
|
||||
|
||||
company_lft, company_rgt = frappe.get_cached_value('Company',
|
||||
company_lft, company_rgt = frappe.get_cached_value('Company',
|
||||
filters.get('company'), ["lft", "rgt"])
|
||||
|
||||
additional_conditions = get_additional_conditions(from_date, ignore_closing_entries, filters)
|
||||
@ -387,10 +388,10 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters):
|
||||
company_finance_book = erpnext.get_default_finance_book(filters.get("company"))
|
||||
|
||||
if not filters.get('finance_book') or (filters.get('finance_book') == company_finance_book):
|
||||
additional_conditions.append("ifnull(finance_book, '') in ('%s', '')" %
|
||||
additional_conditions.append("ifnull(finance_book, '') in (%s, '')" %
|
||||
frappe.db.escape(company_finance_book))
|
||||
elif filters.get("finance_book"):
|
||||
additional_conditions.append("ifnull(finance_book, '') = '%s' " %
|
||||
additional_conditions.append("ifnull(finance_book, '') = %s " %
|
||||
frappe.db.escape(filters.get("finance_book")))
|
||||
|
||||
return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else ""
|
||||
|
@ -343,8 +343,8 @@ def set_gl_entries_by_account(
|
||||
|
||||
accounts = frappe.db.sql_list("""select name from `tabAccount`
|
||||
where lft >= %s and rgt <= %s""", (root_lft, root_rgt))
|
||||
additional_conditions += " and account in ('{}')"\
|
||||
.format("', '".join([frappe.db.escape(d) for d in accounts]))
|
||||
additional_conditions += " and account in ({})"\
|
||||
.format(", ".join([frappe.db.escape(d) for d in accounts]))
|
||||
|
||||
gl_entries = frappe.db.sql("""select posting_date, account, debit, credit, is_opening, fiscal_year, debit_in_account_currency, credit_in_account_currency, account_currency from `tabGL Entry`
|
||||
where company=%(company)s
|
||||
@ -392,10 +392,10 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters):
|
||||
company_finance_book = erpnext.get_default_finance_book(filters.get("company"))
|
||||
|
||||
if not filters.get('finance_book') or (filters.get('finance_book') == company_finance_book):
|
||||
additional_conditions.append("ifnull(finance_book, '') in ('%s', '')" %
|
||||
additional_conditions.append("ifnull(finance_book, '') in (%s, '')" %
|
||||
frappe.db.escape(company_finance_book))
|
||||
elif filters.get("finance_book"):
|
||||
additional_conditions.append("ifnull(finance_book, '') = '%s' " %
|
||||
additional_conditions.append("ifnull(finance_book, '') = %s " %
|
||||
frappe.db.escape(filters.get("finance_book")))
|
||||
|
||||
return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else ""
|
||||
|
@ -99,7 +99,7 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, company
|
||||
|
||||
cond = []
|
||||
if date:
|
||||
cond.append("posting_date <= '%s'" % frappe.db.escape(cstr(date)))
|
||||
cond.append("posting_date <= %s" % frappe.db.escape(cstr(date)))
|
||||
else:
|
||||
# get balance of all entries that exist
|
||||
date = nowdate()
|
||||
@ -127,7 +127,7 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, company
|
||||
)""" % (cc.lft, cc.rgt))
|
||||
|
||||
else:
|
||||
cond.append("""gle.cost_center = "%s" """ % (frappe.db.escape(cost_center, percent=False), ))
|
||||
cond.append("""gle.cost_center = %s """ % (frappe.db.escape(cost_center, percent=False), ))
|
||||
|
||||
|
||||
if account:
|
||||
@ -158,14 +158,14 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, company
|
||||
if acc.account_currency == frappe.get_cached_value('Company', acc.company, "default_currency"):
|
||||
in_account_currency = False
|
||||
else:
|
||||
cond.append("""gle.account = "%s" """ % (frappe.db.escape(account, percent=False), ))
|
||||
cond.append("""gle.account = %s """ % (frappe.db.escape(account, percent=False), ))
|
||||
|
||||
if party_type and party:
|
||||
cond.append("""gle.party_type = "%s" and gle.party = "%s" """ %
|
||||
cond.append("""gle.party_type = %s and gle.party = %s """ %
|
||||
(frappe.db.escape(party_type), frappe.db.escape(party, percent=False)))
|
||||
|
||||
if company:
|
||||
cond.append("""gle.company = "%s" """ % (frappe.db.escape(company, percent=False)))
|
||||
cond.append("""gle.company = %s """ % (frappe.db.escape(company, percent=False)))
|
||||
|
||||
if account or (party_type and party):
|
||||
if in_account_currency:
|
||||
@ -183,7 +183,7 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, company
|
||||
def get_count_on(account, fieldname, date):
|
||||
cond = []
|
||||
if date:
|
||||
cond.append("posting_date <= '%s'" % frappe.db.escape(cstr(date)))
|
||||
cond.append("posting_date <= %s" % frappe.db.escape(cstr(date)))
|
||||
else:
|
||||
# get balance of all entries that exist
|
||||
date = nowdate()
|
||||
@ -218,7 +218,7 @@ def get_count_on(account, fieldname, date):
|
||||
and ac.lft >= %s and ac.rgt <= %s
|
||||
)""" % (acc.lft, acc.rgt))
|
||||
else:
|
||||
cond.append("""gle.account = "%s" """ % (frappe.db.escape(account, percent=False), ))
|
||||
cond.append("""gle.account = %s """ % (frappe.db.escape(account, percent=False), ))
|
||||
|
||||
entries = frappe.db.sql("""
|
||||
SELECT name, posting_date, account, party_type, party,debit,credit,
|
||||
|
@ -203,12 +203,11 @@ def get_children(doctype, parent=None, location=None, is_root=False):
|
||||
from
|
||||
`tab{doctype}` comp
|
||||
where
|
||||
ifnull(parent_location, "")="{parent}"
|
||||
ifnull(parent_location, "")={parent}
|
||||
""".format(
|
||||
doctype=frappe.db.escape(doctype),
|
||||
parent=frappe.db.escape(parent)
|
||||
), as_dict=1)
|
||||
|
||||
doctype=doctype,
|
||||
parent=frappe.db.escape(parent)
|
||||
), as_dict=1)
|
||||
|
||||
@frappe.whitelist()
|
||||
def add_node():
|
||||
|
@ -42,6 +42,7 @@ frappe.ui.form.on("Purchase Order", {
|
||||
frm: frm,
|
||||
child_docname: "items",
|
||||
child_doctype: "Purchase Order Detail",
|
||||
cannot_add_row: false,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
@ -182,7 +182,7 @@ class PurchaseOrder(BuyingController):
|
||||
def check_modified_date(self):
|
||||
mod_db = frappe.db.sql("select modified from `tabPurchase Order` where name = %s",
|
||||
self.name)
|
||||
date_diff = frappe.db.sql("select TIMEDIFF('%s', '%s')" % ( mod_db[0][0],cstr(self.modified)))
|
||||
date_diff = frappe.db.sql("select '%s' - '%s' " % (mod_db[0][0], cstr(self.modified)))
|
||||
|
||||
if date_diff and date_diff[0][0]:
|
||||
msgprint(_("{0} {1} has been modified. Please refresh.").format(self.doctype, self.name),
|
||||
@ -295,7 +295,10 @@ class PurchaseOrder(BuyingController):
|
||||
for item in self.items:
|
||||
received_qty += item.received_qty
|
||||
total_qty += item.qty
|
||||
self.db_set("per_received", flt(received_qty/total_qty) * 100, update_modified=False)
|
||||
if total_qty:
|
||||
self.db_set("per_received", flt(received_qty/total_qty) * 100, update_modified=False)
|
||||
else:
|
||||
self.db_set("per_received", 0, update_modified=False)
|
||||
|
||||
def item_last_purchase_rate(name, conversion_rate, item_code, conversion_factor= 1.0):
|
||||
"""get last purchase rate for an item"""
|
||||
|
@ -77,9 +77,20 @@ frappe.query_reports["Purchase Analytics"] = {
|
||||
events: {
|
||||
onCheckRow: function(data) {
|
||||
row_name = data[2].content;
|
||||
row_values = data.slice(5).map(function (column) {
|
||||
return column.content;
|
||||
})
|
||||
length = data.length;
|
||||
|
||||
var tree_type = frappe.query_report.filters[0].value;
|
||||
|
||||
if(tree_type == "Supplier" || tree_type == "Item") {
|
||||
row_values = data.slice(4,length-1).map(function (column) {
|
||||
return column.content;
|
||||
})
|
||||
}
|
||||
else {
|
||||
row_values = data.slice(3,length-1).map(function (column) {
|
||||
return column.content;
|
||||
})
|
||||
}
|
||||
|
||||
entry = {
|
||||
'name':row_name,
|
||||
|
@ -6,6 +6,7 @@ import frappe, erpnext
|
||||
import json
|
||||
from frappe import _, throw
|
||||
from frappe.utils import today, flt, cint, fmt_money, formatdate, getdate, add_days, add_months, get_last_day, nowdate
|
||||
from erpnext.stock.get_item_details import get_conversion_factor
|
||||
from erpnext.setup.utils import get_exchange_rate
|
||||
from erpnext.accounts.utils import get_fiscal_years, validate_fiscal_year, get_account_currency
|
||||
from erpnext.utilities.transaction_base import TransactionBase
|
||||
@ -1067,24 +1068,68 @@ def get_supplier_block_status(party_name):
|
||||
}
|
||||
return info
|
||||
|
||||
def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_code):
|
||||
"""
|
||||
Returns a Sales Order Item child item containing the default values
|
||||
"""
|
||||
p_doctype = frappe.get_doc(parent_doctype, parent_doctype_name)
|
||||
child_item = frappe.new_doc('Sales Order Item', p_doctype, child_docname)
|
||||
item = frappe.get_doc("Item", item_code)
|
||||
child_item.item_code = item.item_code
|
||||
child_item.item_name = item.item_name
|
||||
child_item.description = item.description
|
||||
child_item.reqd_by_date = p_doctype.delivery_date
|
||||
child_item.uom = item.stock_uom
|
||||
child_item.conversion_factor = get_conversion_factor(item_code, item.stock_uom).get("conversion_factor") or 1.0
|
||||
return child_item
|
||||
|
||||
|
||||
def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_code):
|
||||
"""
|
||||
Returns a Purchase Order Item child item containing the default values
|
||||
"""
|
||||
p_doctype = frappe.get_doc(parent_doctype, parent_doctype_name)
|
||||
child_item = frappe.new_doc('Purchase Order Item', p_doctype, child_docname)
|
||||
item = frappe.get_doc("Item", item_code)
|
||||
child_item.item_code = item.item_code
|
||||
child_item.item_name = item.item_name
|
||||
child_item.description = item.description
|
||||
child_item.schedule_date = p_doctype.schedule_date
|
||||
child_item.uom = item.stock_uom
|
||||
child_item.conversion_factor = get_conversion_factor(item_code, item.stock_uom).get("conversion_factor") or 1.0
|
||||
child_item.base_rate = 1 # Initiallize value will update in parent validation
|
||||
child_item.base_amount = 1 # Initiallize value will update in parent validation
|
||||
return child_item
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name):
|
||||
def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"):
|
||||
data = json.loads(trans_items)
|
||||
for d in data:
|
||||
child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname"))
|
||||
child_item.qty = flt(d.get("qty"))
|
||||
|
||||
for d in data:
|
||||
new_child_flag = False
|
||||
if not d.get("docname"):
|
||||
new_child_flag = True
|
||||
if parent_doctype == "Sales Order":
|
||||
child_item = set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d.get("item_code"))
|
||||
if parent_doctype == "Purchase Order":
|
||||
child_item = set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d.get("item_code"))
|
||||
else:
|
||||
child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname"))
|
||||
|
||||
child_item.qty = flt(d.get("qty"))
|
||||
if child_item.billed_amt > (flt(d.get("rate")) * flt(d.get("qty"))):
|
||||
frappe.throw(_("Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.")
|
||||
.format(child_item.idx, child_item.item_code))
|
||||
else:
|
||||
child_item.rate = flt(d.get("rate"))
|
||||
child_item.flags.ignore_validate_update_after_submit = True
|
||||
child_item.save()
|
||||
if new_child_flag:
|
||||
child_item.insert()
|
||||
else:
|
||||
child_item.save()
|
||||
|
||||
p_doctype = frappe.get_doc(parent_doctype, parent_doctype_name)
|
||||
|
||||
p_doctype.flags.ignore_validate_update_after_submit = True
|
||||
p_doctype.set_qty_as_per_stock_uom()
|
||||
p_doctype.calculate_taxes_and_totals()
|
||||
|
@ -759,7 +759,7 @@ def validate_item_type(doc, fieldname, message):
|
||||
if not items:
|
||||
return
|
||||
|
||||
item_list = ", ".join(["'%s'" % frappe.db.escape(d) for d in items])
|
||||
item_list = ", ".join(["%s" % frappe.db.escape(d) for d in items])
|
||||
|
||||
invalid_items = [d[0] for d in frappe.db.sql("""
|
||||
select item_code from tabItem where name in ({0}) and {1}=0
|
||||
|
@ -119,7 +119,7 @@ def get_attribute_values(item):
|
||||
return frappe.flags.attribute_values, frappe.flags.numeric_values
|
||||
|
||||
def find_variant(template, args, variant_item_code=None):
|
||||
conditions = ["""(iv_attribute.attribute="{0}" and iv_attribute.attribute_value="{1}")"""\
|
||||
conditions = ["""(iv_attribute.attribute={0} and iv_attribute.attribute_value={1})"""\
|
||||
.format(frappe.db.escape(key), frappe.db.escape(cstr(value))) for key, value in args.items()]
|
||||
|
||||
conditions = " or ".join(conditions)
|
||||
|
@ -208,9 +208,8 @@ def bom(doctype, txt, searchfield, start, page_len, filters):
|
||||
limit %(start)s, %(page_len)s """.format(
|
||||
fcond=get_filters_cond(doctype, filters, conditions),
|
||||
mcond=get_match_cond(doctype),
|
||||
key=frappe.db.escape(searchfield)),
|
||||
{
|
||||
'txt': "%%%s%%" % frappe.db.escape(txt),
|
||||
key=searchfield), {
|
||||
'txt': '%' + txt + '%',
|
||||
'_txt': txt.replace("%", ""),
|
||||
'start': start or 0,
|
||||
'page_len': page_len or 20
|
||||
@ -219,7 +218,7 @@ def bom(doctype, txt, searchfield, start, page_len, filters):
|
||||
def get_project_name(doctype, txt, searchfield, start, page_len, filters):
|
||||
cond = ''
|
||||
if filters.get('customer'):
|
||||
cond = """(`tabProject`.customer = '%s' or
|
||||
cond = """(`tabProject`.customer = %s or
|
||||
ifnull(`tabProject`.customer,"")="") and""" %(frappe.db.escape(filters.get("customer")))
|
||||
|
||||
return frappe.db.sql("""select `tabProject`.name from `tabProject`
|
||||
@ -353,7 +352,7 @@ def get_income_account(doctype, txt, searchfield, start, page_len, filters):
|
||||
{condition} {match_condition}
|
||||
order by idx desc, name"""
|
||||
.format(condition=condition, match_condition=get_match_cond(doctype), key=searchfield), {
|
||||
'txt': "%%%s%%" % frappe.db.escape(txt),
|
||||
'txt': '%' + txt + '%',
|
||||
'company': filters.get("company", "")
|
||||
})
|
||||
|
||||
@ -375,10 +374,10 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters):
|
||||
and tabAccount.docstatus!=2
|
||||
and tabAccount.{key} LIKE %(txt)s
|
||||
{condition} {match_condition}"""
|
||||
.format(condition=condition, key=frappe.db.escape(searchfield),
|
||||
.format(condition=condition, key=searchfield,
|
||||
match_condition=get_match_cond(doctype)), {
|
||||
'company': filters.get("company", ""),
|
||||
'txt': "%%%s%%" % frappe.db.escape(txt)
|
||||
'txt': '%' + txt + '%'
|
||||
})
|
||||
|
||||
|
||||
@ -398,7 +397,7 @@ def warehouse_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
CONCAT_WS(" : ", "Actual Qty", ifnull( ({sub_query}), 0) ) as actual_qty
|
||||
from `tabWarehouse`
|
||||
where
|
||||
`tabWarehouse`.`{key}` like '{txt}'
|
||||
`tabWarehouse`.`{key}` like {txt}
|
||||
{fcond} {mcond}
|
||||
order by
|
||||
`tabWarehouse`.name desc
|
||||
@ -406,7 +405,7 @@ def warehouse_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
{start}, {page_len}
|
||||
""".format(
|
||||
sub_query=sub_query,
|
||||
key=frappe.db.escape(searchfield),
|
||||
key=searchfield,
|
||||
fcond=get_filters_cond(doctype, filter_dict.get("Warehouse"), conditions),
|
||||
mcond=get_match_cond(doctype),
|
||||
start=start,
|
||||
@ -430,9 +429,9 @@ def get_batch_numbers(doctype, txt, searchfield, start, page_len, filters):
|
||||
query = """select batch_id from `tabBatch`
|
||||
where disabled = 0
|
||||
and (expiry_date >= CURDATE() or expiry_date IS NULL)
|
||||
and name like '{txt}'""".format(txt = frappe.db.escape('%{0}%'.format(txt)))
|
||||
and name like {txt}""".format(txt = frappe.db.escape('%{0}%'.format(txt)))
|
||||
|
||||
if filters and filters.get('item'):
|
||||
query += " and item = '{item}'".format(item = frappe.db.escape(filters.get('item')))
|
||||
query += " and item = {item}".format(item = frappe.db.escape(filters.get('item')))
|
||||
|
||||
return frappe.db.sql(query, filters)
|
||||
|
@ -244,6 +244,7 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
||||
|
||||
def update_item(source_doc, target_doc, source_parent):
|
||||
target_doc.qty = -1* source_doc.qty
|
||||
default_return_warehouse = frappe.db.get_single_value("Stock Settings", "default_return_warehouse")
|
||||
if doctype == "Purchase Receipt":
|
||||
target_doc.received_qty = -1* source_doc.received_qty
|
||||
target_doc.rejected_qty = -1* source_doc.rejected_qty
|
||||
@ -268,6 +269,7 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
||||
target_doc.so_detail = source_doc.so_detail
|
||||
target_doc.si_detail = source_doc.si_detail
|
||||
target_doc.expense_account = source_doc.expense_account
|
||||
target_doc.warehouse = default_return_warehouse if default_return_warehouse else source_doc.warehouse
|
||||
elif doctype == "Sales Invoice":
|
||||
target_doc.sales_order = source_doc.sales_order
|
||||
target_doc.delivery_note = source_doc.delivery_note
|
||||
|
@ -308,7 +308,7 @@ class StatusUpdater(Document):
|
||||
def _update_modified(self, args, update_modified):
|
||||
args['update_modified'] = ''
|
||||
if update_modified:
|
||||
args['update_modified'] = ', modified = now(), modified_by = "{0}"'\
|
||||
args['update_modified'] = ', modified = now(), modified_by = {0}'\
|
||||
.format(frappe.db.escape(frappe.session.user))
|
||||
|
||||
def update_billing_status_for_zero_amount_refdoc(self, ref_dt):
|
||||
|
@ -272,6 +272,8 @@ class calculate_taxes_and_totals(object):
|
||||
elif tax.charge_type == "On Previous Row Total":
|
||||
current_tax_amount = (tax_rate / 100.0) * \
|
||||
self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_for_current_item
|
||||
elif tax.charge_type == "On Item Quantity":
|
||||
current_tax_amount = tax_rate * item.stock_qty
|
||||
|
||||
self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
|
||||
|
||||
|
94
erpnext/controllers/tests/test_qty_based_taxes.py
Normal file
94
erpnext/controllers/tests/test_qty_based_taxes.py
Normal file
@ -0,0 +1,94 @@
|
||||
from __future__ import unicode_literals, print_function
|
||||
import unittest
|
||||
import frappe
|
||||
from uuid import uuid4 as _uuid4
|
||||
|
||||
def uuid4():
|
||||
return str(_uuid4())
|
||||
|
||||
class TestTaxes(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.company = frappe.get_doc({
|
||||
'doctype': 'Company',
|
||||
'company_name': uuid4(),
|
||||
'abbr': ''.join(s[0] for s in uuid4().split('-')),
|
||||
'default_currency': 'USD',
|
||||
'country': 'United States',
|
||||
}).insert()
|
||||
self.account = frappe.get_doc({
|
||||
'doctype': 'Account',
|
||||
'account_name': uuid4(),
|
||||
'account_type': 'Tax',
|
||||
'company': self.company.name,
|
||||
'parent_account': 'Duties and Taxes - {self.company.abbr}'.format(self=self)
|
||||
}).insert()
|
||||
self.item_group = frappe.get_doc({
|
||||
'doctype': 'Item Group',
|
||||
'item_group_name': uuid4(),
|
||||
'parent_item_group': 'All Item Groups',
|
||||
}).insert()
|
||||
self.item = frappe.get_doc({
|
||||
'doctype': 'Item',
|
||||
'item_code': uuid4(),
|
||||
'item_group': self.item_group.name,
|
||||
'is_stock_item': 0,
|
||||
'taxes': [
|
||||
{
|
||||
'tax_type': self.account.name,
|
||||
'tax_rate': 2,
|
||||
}
|
||||
],
|
||||
}).insert()
|
||||
self.customer = frappe.get_doc({
|
||||
'doctype': 'Customer',
|
||||
'customer_name': uuid4(),
|
||||
'customer_group': 'All Customer Groups',
|
||||
}).insert()
|
||||
self.supplier = frappe.get_doc({
|
||||
'doctype': 'Supplier',
|
||||
'supplier_name': uuid4(),
|
||||
'supplier_group': 'All Supplier Groups',
|
||||
}).insert()
|
||||
|
||||
def test_taxes(self):
|
||||
self.created_docs = []
|
||||
for dt in ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice',
|
||||
'Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']:
|
||||
doc = frappe.get_doc({
|
||||
'doctype': dt,
|
||||
'company': self.company.name,
|
||||
'supplier': self.supplier.name,
|
||||
'schedule_date': frappe.utils.nowdate(),
|
||||
'delivery_date': frappe.utils.nowdate(),
|
||||
'customer': self.customer.name,
|
||||
'buying_price_list' if dt.startswith('Purchase') else 'selling_price_list'
|
||||
: 'Standard Buying' if dt.startswith('Purchase') else 'Standard Selling',
|
||||
'items': [
|
||||
{
|
||||
'item_code': self.item.name,
|
||||
'qty': 300,
|
||||
'rate': 100,
|
||||
}
|
||||
],
|
||||
'taxes': [
|
||||
{
|
||||
'charge_type': 'On Item Quantity',
|
||||
'account_head': self.account.name,
|
||||
'description': 'N/A',
|
||||
'rate': 0,
|
||||
},
|
||||
],
|
||||
})
|
||||
doc.run_method('set_missing_values')
|
||||
doc.run_method('calculate_taxes_and_totals')
|
||||
doc.insert()
|
||||
self.assertEqual(doc.taxes[0].tax_amount, 600)
|
||||
self.created_docs.append(doc)
|
||||
|
||||
def tearDown(self):
|
||||
for doc in self.created_docs:
|
||||
doc.delete()
|
||||
self.item.delete()
|
||||
self.item_group.delete()
|
||||
self.account.delete()
|
||||
self.company.delete()
|
@ -117,14 +117,14 @@ def generate_fee(fee_schedule):
|
||||
def get_students(student_group, academic_year, academic_term=None, student_category=None):
|
||||
conditions = ""
|
||||
if student_category:
|
||||
conditions = " and pe.student_category='{}'".format(frappe.db.escape(student_category))
|
||||
conditions = " and pe.student_category={}".format(frappe.db.escape(student_category))
|
||||
if academic_term:
|
||||
conditions = " and pe.academic_term='{}'".format(frappe.db.escape(academic_term))
|
||||
conditions = " and pe.academic_term={}".format(frappe.db.escape(academic_term))
|
||||
|
||||
students = frappe.db.sql("""
|
||||
select pe.student, pe.student_name, pe.program, pe.student_batch_name
|
||||
from `tabStudent Group Student` sgs, `tabProgram Enrollment` pe
|
||||
where
|
||||
where
|
||||
pe.student = sgs.student and pe.academic_year = %s
|
||||
and sgs.parent = %s and sgs.active = 1
|
||||
{conditions}
|
||||
|
@ -12,7 +12,7 @@ app_license = "GNU General Public License (v3)"
|
||||
source_link = "https://github.com/frappe/erpnext"
|
||||
|
||||
develop_version = '12.x.x-develop'
|
||||
staging_version = '11.0.3-beta.30'
|
||||
staging_version = '11.0.3-beta.31'
|
||||
|
||||
error_report_email = "support@erpnext.com"
|
||||
|
||||
@ -133,6 +133,13 @@ website_route_rules = [
|
||||
{"from_route": "/admissions", "to_route": "Student Admission"},
|
||||
{"from_route": "/boms", "to_route": "BOM"},
|
||||
{"from_route": "/timesheets", "to_route": "Timesheet"},
|
||||
{"from_route": "/material-requests", "to_route": "Material Request"},
|
||||
{"from_route": "/material-requests/<path:name>", "to_route": "material_request_info",
|
||||
"defaults": {
|
||||
"doctype": "Material Request",
|
||||
"parents": [{"label": _("Material Request"), "route": "material-requests"}]
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
standard_portal_menu_items = [
|
||||
@ -155,6 +162,7 @@ standard_portal_menu_items = [
|
||||
{"title": _("Newsletter"), "route": "/newsletters", "reference_doctype": "Newsletter"},
|
||||
{"title": _("Admission"), "route": "/admissions", "reference_doctype": "Student Admission"},
|
||||
{"title": _("Certification"), "route": "/certification", "reference_doctype": "Certification Application"},
|
||||
{"title": _("Material Request"), "route": "/material-requests", "reference_doctype": "Material Request", "role": "Customer"},
|
||||
]
|
||||
|
||||
default_roles = [
|
||||
@ -168,6 +176,7 @@ has_website_permission = {
|
||||
"Quotation": "erpnext.controllers.website_list_for_contact.has_website_permission",
|
||||
"Sales Invoice": "erpnext.controllers.website_list_for_contact.has_website_permission",
|
||||
"Supplier Quotation": "erpnext.controllers.website_list_for_contact.has_website_permission",
|
||||
"Material Request": "erpnext.controllers.website_list_for_contact.has_website_permission",
|
||||
"Delivery Note": "erpnext.controllers.website_list_for_contact.has_website_permission",
|
||||
"Issue": "erpnext.support.doctype.issue.issue.has_website_permission",
|
||||
"Timesheet": "erpnext.controllers.website_list_for_contact.has_website_permission",
|
||||
|
@ -59,15 +59,15 @@ class HotelRoomReservation(Document):
|
||||
if not d.item:
|
||||
continue
|
||||
day_rate = frappe.db.sql("""
|
||||
select
|
||||
item.rate
|
||||
from
|
||||
select
|
||||
item.rate
|
||||
from
|
||||
`tabHotel Room Pricing Item` item,
|
||||
`tabHotel Room Pricing` pricing
|
||||
where
|
||||
item.parent = pricing.name
|
||||
and item.item = %s
|
||||
and %s between pricing.from_date
|
||||
and %s between pricing.from_date
|
||||
and pricing.to_date""", (d.item, day))
|
||||
|
||||
if day_rate:
|
||||
@ -90,7 +90,7 @@ def get_room_rate(hotel_room_reservation):
|
||||
def get_rooms_booked(room_type, day, exclude_reservation=None):
|
||||
exclude_condition = ''
|
||||
if exclude_reservation:
|
||||
exclude_condition = 'and reservation.name != "{0}"'.format(frappe.db.escape(exclude_reservation))
|
||||
exclude_condition = 'and reservation.name != {0}'.format(frappe.db.escape(exclude_reservation))
|
||||
|
||||
return frappe.db.sql("""
|
||||
select sum(item.qty)
|
||||
@ -105,5 +105,5 @@ def get_rooms_booked(room_type, day, exclude_reservation=None):
|
||||
and reservation.docstatus = 1
|
||||
{exclude_condition}
|
||||
and %s between reservation.from_date
|
||||
and reservation.to_date""".format(exclude_condition=exclude_condition),
|
||||
and reservation.to_date""".format(exclude_condition=exclude_condition),
|
||||
(room_type, day))[0][0] or 0
|
||||
|
@ -13,7 +13,7 @@ from frappe.utils import cstr
|
||||
class Attendance(Document):
|
||||
def validate_duplicate_record(self):
|
||||
res = frappe.db.sql("""select name from `tabAttendance` where employee = %s and attendance_date = %s
|
||||
and name != %s and docstatus = 1""",
|
||||
and name != %s and docstatus != 2""",
|
||||
(self.employee, self.attendance_date, self.name))
|
||||
if res:
|
||||
frappe.throw(_("Attendance for employee {0} is already marked").format(self.employee))
|
||||
@ -89,4 +89,4 @@ def add_attendance(events, start, end, conditions=None):
|
||||
"docstatus": d.docstatus
|
||||
}
|
||||
if e not in events:
|
||||
events.append(e)
|
||||
events.append(e)
|
||||
|
@ -2,7 +2,28 @@
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Employee Grade', {
|
||||
refresh: function(frm) {
|
||||
refresh: function (frm) {
|
||||
|
||||
},
|
||||
setup: function (frm) {
|
||||
frm.set_query("default_salary_structure", function () {
|
||||
return {
|
||||
"filters": {
|
||||
"docstatus": 1,
|
||||
"is_active": "Yes"
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query("default_leave_policy", function () {
|
||||
return {
|
||||
"filters": {
|
||||
"docstatus": 1
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
@ -15,150 +15,153 @@
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "default_leave_policy",
|
||||
"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": "Default Leave Policy",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Leave Policy",
|
||||
"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,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "default_leave_policy",
|
||||
"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": "Default Leave Policy",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Leave Policy",
|
||||
"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_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "default_salary_structure",
|
||||
"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": "Default Salary Structure",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Salary Structure",
|
||||
"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,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "default_salary_structure",
|
||||
"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": "Default Salary Structure",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Salary Structure",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-04-13 16:14:24.174138",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Employee Grade",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-09-18 17:17:45.617624",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Employee Grade",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "HR User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
@ -18,7 +18,7 @@ class ExpenseApproverIdentityError(frappe.ValidationError): pass
|
||||
|
||||
class ExpenseClaim(AccountsController):
|
||||
def onload(self):
|
||||
self.get("__onload").make_payment_via_journal_entry = frappe.db.get_single_value('Accounts Settings',
|
||||
self.get("__onload").make_payment_via_journal_entry = frappe.db.get_single_value('Accounts Settings',
|
||||
'make_payment_via_journal_entry')
|
||||
|
||||
def validate(self):
|
||||
@ -103,7 +103,7 @@ class ExpenseClaim(AccountsController):
|
||||
self.validate_account_details()
|
||||
|
||||
payable_amount = flt(self.total_sanctioned_amount) - flt(self.total_advance_amount)
|
||||
|
||||
|
||||
# payable entry
|
||||
if payable_amount:
|
||||
gl_entry.append(
|
||||
@ -233,7 +233,7 @@ class ExpenseClaim(AccountsController):
|
||||
expense.default_account = get_expense_claim_account(expense.expense_type, self.company)["account"]
|
||||
|
||||
def update_reimbursed_amount(doc):
|
||||
amt = frappe.db.sql("""select ifnull(sum(debit_in_account_currency), 0) as amt
|
||||
amt = frappe.db.sql("""select ifnull(sum(debit_in_account_currency), 0) as amt
|
||||
from `tabGL Entry` where against_voucher_type = 'Expense Claim' and against_voucher = %s
|
||||
and party = %s """, (doc.name, doc.employee) ,as_dict=1)[0].amt
|
||||
|
||||
@ -288,7 +288,7 @@ def get_expense_claim_account(expense_claim_type, company):
|
||||
if not account:
|
||||
frappe.throw(_("Please set default account in Expense Claim Type {0}")
|
||||
.format(expense_claim_type))
|
||||
|
||||
|
||||
return {
|
||||
"account": account
|
||||
}
|
||||
@ -296,14 +296,14 @@ def get_expense_claim_account(expense_claim_type, company):
|
||||
@frappe.whitelist()
|
||||
def get_advances(employee, advance_id=None):
|
||||
if not advance_id:
|
||||
condition = 'docstatus=1 and employee="{0}" and paid_amount > 0 and paid_amount > claimed_amount'.format(frappe.db.escape(employee))
|
||||
condition = 'docstatus=1 and employee={0} and paid_amount > 0 and paid_amount > claimed_amount'.format(frappe.db.escape(employee))
|
||||
else:
|
||||
condition = 'name="{0}"'.format(frappe.db.escape(advance_id))
|
||||
condition = 'name={0}'.format(frappe.db.escape(advance_id))
|
||||
|
||||
return frappe.db.sql("""
|
||||
select
|
||||
select
|
||||
name, posting_date, paid_amount, claimed_amount, advance_account
|
||||
from
|
||||
from
|
||||
`tabEmployee Advance`
|
||||
where {0}
|
||||
""".format(condition), as_dict=1)
|
||||
|
@ -20,6 +20,10 @@ class PayrollEntry(Document):
|
||||
if self.validate_employee_attendance():
|
||||
frappe.throw(_("Cannot Submit, Employees left to mark attendance"))
|
||||
|
||||
def on_cancel(self):
|
||||
frappe.delete_doc("Salary Slip", frappe.db.sql_list("""select name from `tabSalary Slip`
|
||||
where payroll_entry=%s """, (self.name)))
|
||||
|
||||
def get_emp_list(self):
|
||||
"""
|
||||
Returns list of active employees based on selected criteria
|
||||
|
@ -162,28 +162,30 @@ class BOM(WebsiteGenerator):
|
||||
if arg.get('scrap_items'):
|
||||
rate = self.get_valuation_rate(arg)
|
||||
elif arg:
|
||||
if arg.get('bom_no') and self.set_rate_of_sub_assembly_item_based_on_bom:
|
||||
rate = self.get_bom_unitcost(arg['bom_no'])
|
||||
else:
|
||||
if self.rm_cost_as_per == 'Valuation Rate':
|
||||
rate = self.get_valuation_rate(arg)
|
||||
elif self.rm_cost_as_per == 'Last Purchase Rate':
|
||||
rate = arg.get('last_purchase_rate') \
|
||||
or frappe.db.get_value("Item", arg['item_code'], "last_purchase_rate")
|
||||
elif self.rm_cost_as_per == "Price List":
|
||||
if not self.buying_price_list:
|
||||
frappe.throw(_("Please select Price List"))
|
||||
rate = frappe.db.get_value("Item Price", {"price_list": self.buying_price_list,
|
||||
"item_code": arg["item_code"]}, "price_list_rate") or 0.0
|
||||
#Customer Provided parts will have zero rate
|
||||
if not frappe.db.get_value('Item', arg["item_code"], 'is_customer_provided_item'):
|
||||
if arg.get('bom_no') and self.set_rate_of_sub_assembly_item_based_on_bom:
|
||||
rate = self.get_bom_unitcost(arg['bom_no'])
|
||||
else:
|
||||
if self.rm_cost_as_per == 'Valuation Rate':
|
||||
rate = self.get_valuation_rate(arg)
|
||||
elif self.rm_cost_as_per == 'Last Purchase Rate':
|
||||
rate = arg.get('last_purchase_rate') \
|
||||
or frappe.db.get_value("Item", arg['item_code'], "last_purchase_rate")
|
||||
elif self.rm_cost_as_per == "Price List":
|
||||
if not self.buying_price_list:
|
||||
frappe.throw(_("Please select Price List"))
|
||||
rate = frappe.db.get_value("Item Price", {"price_list": self.buying_price_list,
|
||||
"item_code": arg["item_code"]}, "price_list_rate") or 0.0
|
||||
|
||||
price_list_currency = frappe.db.get_value("Price List",
|
||||
self.buying_price_list, "currency")
|
||||
if price_list_currency != self.company_currency():
|
||||
rate = flt(rate * self.conversion_rate)
|
||||
price_list_currency = frappe.db.get_value("Price List",
|
||||
self.buying_price_list, "currency")
|
||||
if price_list_currency != self.company_currency():
|
||||
rate = flt(rate * self.conversion_rate)
|
||||
|
||||
if not rate:
|
||||
frappe.msgprint(_("{0} not found for Item {1}")
|
||||
.format(self.rm_cost_as_per, arg["item_code"]), alert=True)
|
||||
if not rate:
|
||||
frappe.msgprint(_("{0} not found for Item {1}")
|
||||
.format(self.rm_cost_as_per, arg["item_code"]), alert=True)
|
||||
|
||||
return flt(rate)
|
||||
|
||||
|
@ -131,4 +131,4 @@ class TestBOM(unittest.TestCase):
|
||||
self.assertEqual(bom.base_total_cost, 33000)
|
||||
|
||||
def get_default_bom(item_code="_Test FG Item 2"):
|
||||
return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})
|
||||
return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})
|
@ -104,7 +104,7 @@ class ProductionPlan(Document):
|
||||
|
||||
item_condition = ""
|
||||
if self.item_code:
|
||||
item_condition = ' and so_item.item_code = "{0}"'.format(frappe.db.escape(self.item_code))
|
||||
item_condition = ' and so_item.item_code = {0}'.format(frappe.db.escape(self.item_code))
|
||||
|
||||
items = frappe.db.sql("""select distinct parent, item_code, warehouse,
|
||||
(qty - work_order_qty) * conversion_factor as pending_qty, name
|
||||
@ -115,7 +115,7 @@ class ProductionPlan(Document):
|
||||
(", ".join(["%s"] * len(so_list)), item_condition), tuple(so_list), as_dict=1)
|
||||
|
||||
if self.item_code:
|
||||
item_condition = ' and so_item.item_code = "{0}"'.format(frappe.db.escape(self.item_code))
|
||||
item_condition = ' and so_item.item_code = {0}'.format(frappe.db.escape(self.item_code))
|
||||
|
||||
packed_items = frappe.db.sql("""select distinct pi.parent, pi.item_code, pi.warehouse as warehouse,
|
||||
(((so_item.qty - so_item.work_order_qty) * pi.qty) / so_item.qty)
|
||||
@ -139,7 +139,7 @@ class ProductionPlan(Document):
|
||||
|
||||
item_condition = ""
|
||||
if self.item_code:
|
||||
item_condition = " and mr_item.item_code ='{0}'".format(frappe.db.escape(self.item_code))
|
||||
item_condition = " and mr_item.item_code ={0}".format(frappe.db.escape(self.item_code))
|
||||
|
||||
items = frappe.db.sql("""select distinct parent, name, item_code, warehouse,
|
||||
(qty - ordered_qty) as pending_qty
|
||||
@ -325,8 +325,8 @@ class ProductionPlan(Document):
|
||||
for item in self.mr_items:
|
||||
item_doc = frappe.get_cached_doc('Item', item.item_code)
|
||||
|
||||
# key for Sales Order:Material Request Type
|
||||
key = '{}:{}'.format(item.sales_order, item_doc.default_material_request_type)
|
||||
# key for Sales Order:Material Request Type:Customer
|
||||
key = '{}:{}:{}'.format(item.sales_order, item_doc.default_material_request_type,item_doc.customer or '')
|
||||
schedule_date = add_days(nowdate(), cint(item_doc.lead_time_days))
|
||||
|
||||
if not key in material_request_map:
|
||||
@ -338,7 +338,8 @@ class ProductionPlan(Document):
|
||||
"status": "Draft",
|
||||
"company": self.company,
|
||||
"requested_by": frappe.session.user,
|
||||
'material_request_type': item_doc.default_material_request_type
|
||||
'material_request_type': item_doc.default_material_request_type,
|
||||
'customer': item_doc.customer or ''
|
||||
})
|
||||
material_request_list.append(material_request)
|
||||
else:
|
||||
@ -499,7 +500,7 @@ def get_bin_details(row):
|
||||
conditions = ""
|
||||
warehouse = row.source_warehouse or row.default_warehouse or row.warehouse
|
||||
if warehouse:
|
||||
conditions = " and warehouse='{0}'".format(frappe.db.escape(warehouse))
|
||||
conditions = " and warehouse={0}".format(frappe.db.escape(warehouse))
|
||||
|
||||
item_projected_qty = frappe.db.sql(""" select ifnull(sum(projected_qty),0) as projected_qty,
|
||||
ifnull(sum(actual_qty),0) as actual_qty from `tabBin`
|
||||
|
@ -143,12 +143,27 @@ class TestProductionPlan(unittest.TestCase):
|
||||
|
||||
self.assertEqual(sales_orders, [])
|
||||
|
||||
def test_pp_to_mr_customer_provided(self):
|
||||
#Material Request from Production Plan for Customer Provided
|
||||
create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0)
|
||||
create_item('Production Item CUST')
|
||||
for item, raw_materials in {'Production Item CUST': ['Raw Material Item 1', 'CUST-0987']}.items():
|
||||
if not frappe.db.get_value('BOM', {'item': item}):
|
||||
make_bom(item = item, raw_materials = raw_materials)
|
||||
production_plan = create_production_plan(item_code = 'Production Item CUST')
|
||||
production_plan.make_material_request()
|
||||
material_request = frappe.get_value('Material Request Item', {'production_plan': production_plan.name}, 'parent')
|
||||
mr = frappe.get_doc('Material Request', material_request)
|
||||
self.assertTrue(mr.material_request_type, 'Customer Provided')
|
||||
self.assertTrue(mr.customer, '_Test Customer')
|
||||
|
||||
def create_production_plan(**args):
|
||||
args = frappe._dict(args)
|
||||
|
||||
pln = frappe.get_doc({
|
||||
'doctype': 'Production Plan',
|
||||
'company': args.company or '_Test Company',
|
||||
'customer': args.customer or '_Test Customer',
|
||||
'posting_date': nowdate(),
|
||||
'include_non_stock_items': args.include_non_stock_items or 1,
|
||||
'include_subcontracted_items': args.include_subcontracted_items or 1,
|
||||
|
@ -127,7 +127,7 @@ class ProductionPlanningTool(Document):
|
||||
|
||||
item_condition = ""
|
||||
if self.fg_item:
|
||||
item_condition = ' and so_item.item_code = "{0}"'.format(frappe.db.escape(self.fg_item))
|
||||
item_condition = ' and so_item.item_code = {0}'.format(frappe.db.escape(self.fg_item))
|
||||
|
||||
items = frappe.db.sql("""select distinct parent, item_code, warehouse,
|
||||
(qty - delivered_qty)*conversion_factor as pending_qty
|
||||
@ -138,7 +138,7 @@ class ProductionPlanningTool(Document):
|
||||
(", ".join(["%s"] * len(so_list)), item_condition), tuple(so_list), as_dict=1)
|
||||
|
||||
if self.fg_item:
|
||||
item_condition = ' and pi.item_code = "{0}"'.format(frappe.db.escape(self.fg_item))
|
||||
item_condition = ' and pi.item_code = {0}'.format(frappe.db.escape(self.fg_item))
|
||||
|
||||
packed_items = frappe.db.sql("""select distinct pi.parent, pi.item_code, pi.warehouse as warehouse,
|
||||
(((so_item.qty - so_item.delivered_qty) * pi.qty) / so_item.qty)
|
||||
@ -161,7 +161,7 @@ class ProductionPlanningTool(Document):
|
||||
|
||||
item_condition = ""
|
||||
if self.fg_item:
|
||||
item_condition = ' and mr_item.item_code = "' + frappe.db.escape(self.fg_item, percent=False) + '"'
|
||||
item_condition = ' and mr_item.item_code =' + frappe.db.escape(self.fg_item, percent=False)
|
||||
|
||||
items = frappe.db.sql("""select distinct parent, name, item_code, warehouse,
|
||||
(qty - ordered_qty) as pending_qty
|
||||
@ -487,7 +487,7 @@ class ProductionPlanningTool(Document):
|
||||
def get_item_projected_qty(self,item):
|
||||
conditions = ""
|
||||
if self.purchase_request_for_warehouse:
|
||||
conditions = " and warehouse='{0}'".format(frappe.db.escape(self.purchase_request_for_warehouse))
|
||||
conditions = " and warehouse={0}".format(frappe.db.escape(self.purchase_request_for_warehouse))
|
||||
|
||||
item_projected_qty = frappe.db.sql("""
|
||||
select ifnull(sum(projected_qty),0) as qty
|
||||
|
@ -62,7 +62,7 @@ def get_bom_stock(filters):
|
||||
where wh.lft >= %s and wh.rgt <= %s and ledger.warehouse = wh.name)" % (warehouse_details.lft,
|
||||
warehouse_details.rgt)
|
||||
else:
|
||||
conditions += " and ledger.warehouse = '%s'" % frappe.db.escape(filters.get("warehouse"))
|
||||
conditions += " and ledger.warehouse = %s" % frappe.db.escape(filters.get("warehouse"))
|
||||
|
||||
else:
|
||||
conditions += ""
|
||||
|
@ -49,7 +49,7 @@ def get_bom_stock(filters):
|
||||
where wh.lft >= %s and wh.rgt <= %s and ledger.warehouse = wh.name)" % (warehouse_details.lft,
|
||||
warehouse_details.rgt)
|
||||
else:
|
||||
conditions += " and ledger.warehouse = '%s'" % frappe.db.escape(filters.get("warehouse"))
|
||||
conditions += " and ledger.warehouse = %s" % frappe.db.escape(filters.get("warehouse"))
|
||||
|
||||
else:
|
||||
conditions += ""
|
||||
|
@ -15,8 +15,8 @@ def execute():
|
||||
value = frappe.db.escape(frappe.as_unicode(customer.get("customer_group")))
|
||||
|
||||
when_then.append('''
|
||||
WHEN `%s` = "%s" and %s != "%s"
|
||||
THEN "%s"
|
||||
WHEN `%s` = %s and %s != %s
|
||||
THEN %s
|
||||
'''%(d["master_fieldname"], frappe.db.escape(frappe.as_unicode(customer.name)),
|
||||
d["linked_to_fieldname"], value, value))
|
||||
|
||||
|
@ -2,7 +2,7 @@ from __future__ import print_function, unicode_literals
|
||||
import frappe
|
||||
import os
|
||||
from frappe.utils import get_files_path
|
||||
from frappe.utils.file_manager import get_content_hash
|
||||
from frappe.core.doctype.file.file import get_content_hash
|
||||
|
||||
def execute():
|
||||
files_path = get_files_path()
|
||||
|
@ -22,7 +22,7 @@ def execute():
|
||||
condition = ""
|
||||
company = erpnext.get_default_company()
|
||||
if company:
|
||||
condition = " and name='{0}'".format(frappe.db.escape(company))
|
||||
condition = " and name={0}".format(frappe.db.escape(company))
|
||||
|
||||
domains = frappe.db.sql_list("select distinct domain from `tabCompany` where domain != 'Other' {0}".format(condition))
|
||||
|
||||
|
@ -10,9 +10,9 @@ def execute():
|
||||
|
||||
frappe.reload_doc("stock", "doctype", "serial_no")
|
||||
|
||||
frappe.db.sql(""" update `tabSales Invoice Item` sii inner join
|
||||
frappe.db.sql(""" update `tabSales Invoice Item` sii inner join
|
||||
`tabDelivery Note Item` dni on sii.dn_detail=dni.name and sii.qty=dni.qty
|
||||
set sii.serial_no=dni.serial_no where sii.parent IN (select si.name
|
||||
set sii.serial_no=dni.serial_no where sii.parent IN (select si.name
|
||||
from `tabSales Invoice` si where si.update_stock=0 and si.docstatus=1)""")
|
||||
|
||||
items = frappe.db.sql(""" select sii.parent, sii.serial_no from `tabSales Invoice Item` sii
|
||||
@ -26,13 +26,13 @@ def execute():
|
||||
if not sales_invoice or not serial_nos:
|
||||
continue
|
||||
|
||||
serial_nos = ["'%s'"%frappe.db.escape(no) for no in serial_nos.split("\n")]
|
||||
serial_nos = ["{}".format(frappe.db.escape(no)) for no in serial_nos.split("\n")]
|
||||
|
||||
frappe.db.sql("""
|
||||
UPDATE
|
||||
UPDATE
|
||||
`tabSerial No`
|
||||
SET
|
||||
sales_invoice='{sales_invoice}'
|
||||
SET
|
||||
sales_invoice={sales_invoice}
|
||||
WHERE
|
||||
name in ({serial_nos})
|
||||
""".format(
|
||||
|
@ -35,7 +35,7 @@ def execute():
|
||||
else:
|
||||
template = frappe.get_doc("Payment Terms Template", pyt_template_name)
|
||||
|
||||
payment_terms.append('WHEN `name`="%s" THEN "%s"' % (frappe.db.escape(party_name), template.template_name))
|
||||
payment_terms.append('WHEN `name`={0} THEN {1}'.format(frappe.db.escape(party_name), template.template_name))
|
||||
records.append(frappe.db.escape(party_name))
|
||||
|
||||
begin_query_str = "UPDATE `tab{0}` SET `payment_terms` = CASE ".format(doctype)
|
||||
|
@ -168,12 +168,16 @@ def check_if_child_exists(name):
|
||||
def get_project(doctype, txt, searchfield, start, page_len, filters):
|
||||
from erpnext.controllers.queries import get_match_cond
|
||||
return frappe.db.sql(""" select name from `tabProject`
|
||||
where %(key)s like "%(txt)s"
|
||||
where %(key)s like %(txt)s
|
||||
%(mcond)s
|
||||
order by name
|
||||
limit %(start)s, %(page_len)s """ % {'key': searchfield,
|
||||
'txt': "%%%s%%" % frappe.db.escape(txt), 'mcond':get_match_cond(doctype),
|
||||
'start': start, 'page_len': page_len})
|
||||
limit %(start)s, %(page_len)s""" % {
|
||||
'key': searchfield,
|
||||
'txt': frappe.db.escape('%' + txt + '%'),
|
||||
'mcond':get_match_cond(doctype),
|
||||
'start': start,
|
||||
'page_len': page_len
|
||||
})
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
@ -235,7 +235,7 @@ def get_timesheet(doctype, txt, searchfield, start, page_len, filters):
|
||||
and tsd.parent LIKE %(txt)s {condition}
|
||||
order by tsd.parent limit %(start)s, %(page_len)s"""
|
||||
.format(condition=condition), {
|
||||
"txt": "%%%s%%" % frappe.db.escape(txt),
|
||||
'txt': '%' + txt + '%',
|
||||
"start": start, "page_len": page_len, 'project': filters.get("project")
|
||||
})
|
||||
|
||||
|
@ -23,6 +23,6 @@ def query_task(doctype, txt, searchfield, start, page_len, filters):
|
||||
`%s`,
|
||||
subject
|
||||
limit %s, %s""" %
|
||||
(frappe.db.escape(searchfield), "%s", "%s", match_conditions, "%s",
|
||||
frappe.db.escape(searchfield), "%s", frappe.db.escape(searchfield), "%s", "%s"),
|
||||
(searchfield, "%s", "%s", match_conditions, "%s",
|
||||
searchfield, "%s", searchfield, "%s", "%s"),
|
||||
(search_string, search_string, order_by_string, order_by_string, start, page_len))
|
||||
|
@ -68,12 +68,19 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
|
||||
}
|
||||
|
||||
this.frm.set_query("item_code", "items", function() {
|
||||
if(me.frm.doc.is_subcontracted == "Yes") {
|
||||
if (me.frm.doc.is_subcontracted == "Yes") {
|
||||
return{
|
||||
query: "erpnext.controllers.queries.item_query",
|
||||
filters:{ 'is_sub_contracted_item': 1 }
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else if (me.frm.doc.material_request_type == "Customer Provided") {
|
||||
return{
|
||||
query: "erpnext.controllers.queries.item_query",
|
||||
filters:{ 'customer': me.frm.doc.customer }
|
||||
}
|
||||
}
|
||||
else {
|
||||
return{
|
||||
query: "erpnext.controllers.queries.item_query",
|
||||
filters: {'is_purchase_item': 1}
|
||||
|
@ -73,15 +73,13 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
|
||||
if(this.frm.doc.currency == company_currency) {
|
||||
this.frm.set_value("conversion_rate", 1);
|
||||
} else {
|
||||
frappe.throw(repl('%(conversion_rate_label)s' +
|
||||
__(' is mandatory. Maybe Currency Exchange record is not created for ') +
|
||||
'%(from_currency)s' + __(" to ") + '%(to_currency)s', {
|
||||
"conversion_rate_label": conversion_rate_label,
|
||||
"from_currency": this.frm.doc.currency,
|
||||
"to_currency": company_currency
|
||||
}));
|
||||
const err_message = __('{0} is mandatory. Maybe Currency Exchange record is not created for {1} to {2}', [
|
||||
conversion_rate_label,
|
||||
this.frm.doc.currency,
|
||||
company_currency
|
||||
]);
|
||||
frappe.throw(err_message);
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
frappe.provide('erpnext.hub');
|
||||
|
||||
frappe.views.marketplaceFactory = class marketplaceFactory extends frappe.views.Factory {
|
||||
frappe.views.MarketplaceFactory = class MarketplaceFactory extends frappe.views.Factory {
|
||||
show() {
|
||||
is_marketplace_disabled()
|
||||
.then(disabled => {
|
||||
|
@ -406,15 +406,19 @@ erpnext.utils.select_alternate_items = function(opts) {
|
||||
|
||||
erpnext.utils.update_child_items = function(opts) {
|
||||
const frm = opts.frm;
|
||||
|
||||
const cannot_add_row = (typeof opts.cannot_add_row === 'undefined') ? true : opts.cannot_add_row;
|
||||
const child_docname = (typeof opts.cannot_add_row === 'undefined') ? "items" : opts.child_docname;
|
||||
this.data = [];
|
||||
const dialog = new frappe.ui.Dialog({
|
||||
title: __("Update Items"),
|
||||
fields: [
|
||||
{fieldtype:'Section Break', label: __('Items')},
|
||||
{
|
||||
fieldname: "trans_items", fieldtype: "Table", cannot_add_rows: true,
|
||||
in_place_edit: true, data: this.data,
|
||||
fieldname: "trans_items",
|
||||
fieldtype: "Table",
|
||||
cannot_add_rows: cannot_add_row,
|
||||
in_place_edit: true,
|
||||
data: this.data,
|
||||
get_data: () => {
|
||||
return this.data;
|
||||
},
|
||||
@ -450,10 +454,12 @@ erpnext.utils.update_child_items = function(opts) {
|
||||
const trans_items = this.get_values()["trans_items"];
|
||||
frappe.call({
|
||||
method: 'erpnext.controllers.accounts_controller.update_child_qty_rate',
|
||||
freeze: true,
|
||||
args: {
|
||||
'parent_doctype': frm.doc.doctype,
|
||||
'trans_items': trans_items,
|
||||
'parent_doctype_name': frm.doc.name
|
||||
'parent_doctype_name': frm.doc.name,
|
||||
'child_docname': child_docname
|
||||
},
|
||||
callback: function() {
|
||||
frm.reload_doc();
|
||||
|
@ -6,6 +6,6 @@ from frappe import _
|
||||
from erpnext import get_region
|
||||
|
||||
def check_deletion_permission(doc, method):
|
||||
region = get_region()
|
||||
region = get_region(doc.company)
|
||||
if region in ["Nepal", "France"]:
|
||||
frappe.throw(_("Deletion is not permitted for country {0}".format(region)))
|
||||
frappe.throw(_("Deletion is not permitted for country {0}".format(region)))
|
||||
|
@ -117,7 +117,7 @@ class Gstr1Report(object):
|
||||
|
||||
if self.filters.get("type_of_business") == "B2B":
|
||||
conditions += """ and ifnull(invoice_type, '') != 'Export' and is_return != 1
|
||||
and customer in ('{0}')""".format("', '".join([frappe.db.escape(c.name) for c in customers]))
|
||||
and customer in ({0})""".format(", ".join([frappe.db.escape(c.name) for c in customers]))
|
||||
|
||||
if self.filters.get("type_of_business") in ("B2C Large", "B2C Small"):
|
||||
b2c_limit = frappe.db.get_single_value('GSt Settings', 'b2c_limit')
|
||||
@ -126,13 +126,13 @@ class Gstr1Report(object):
|
||||
|
||||
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}')""".\
|
||||
format(flt(b2c_limit), "', '".join([frappe.db.escape(c.name) for c in customers]))
|
||||
and grand_total > {0} and is_return != 1 and customer in ({1})""".\
|
||||
format(flt(b2c_limit), ", ".join([frappe.db.escape(c.name) for c in customers]))
|
||||
elif self.filters.get("type_of_business") == "B2C Small":
|
||||
conditions += """ and (
|
||||
SUBSTR(place_of_supply, 1, 2) = SUBSTR(company_gstin, 1, 2)
|
||||
or grand_total <= {0}) and is_return != 1 and customer in ('{1}')""".\
|
||||
format(flt(b2c_limit), "', '".join([frappe.db.escape(c.name) for c in customers]))
|
||||
or grand_total <= {0}) and is_return != 1 and customer in ({1})""".\
|
||||
format(flt(b2c_limit), ", ".join([frappe.db.escape(c.name) for c in customers]))
|
||||
|
||||
elif self.filters.get("type_of_business") == "CDNR":
|
||||
conditions += """ and is_return = 1 """
|
||||
|
@ -91,7 +91,7 @@ class Customer(TransactionBase):
|
||||
def update_customer_groups(self):
|
||||
ignore_doctypes = ["Lead", "Opportunity", "POS Profile", "Tax Rule", "Pricing Rule"]
|
||||
if frappe.flags.customer_group_changed:
|
||||
update_linked_doctypes('Customer', frappe.db.escape(self.name), 'Customer Group',
|
||||
update_linked_doctypes('Customer', self.name, 'Customer Group',
|
||||
self.customer_group, ignore_doctypes)
|
||||
|
||||
def create_primary_contact(self):
|
||||
|
@ -50,7 +50,7 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({
|
||||
}
|
||||
|
||||
if(doc.docstatus == 1 && doc.status!=='Lost') {
|
||||
if(!doc.valid_till || frappe.datetime.get_diff(doc.valid_till, frappe.datetime.get_today()) > 0) {
|
||||
if(!doc.valid_till || frappe.datetime.get_diff(doc.valid_till, frappe.datetime.get_today()) >= 0) {
|
||||
cur_frm.add_custom_button(__('Sales Order'),
|
||||
cur_frm.cscript['Make Sales Order'], __('Create'));
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ frappe.ui.form.on("Sales Order", {
|
||||
frm: frm,
|
||||
child_docname: "items",
|
||||
child_doctype: "Sales Order Detail",
|
||||
cannot_add_row: false,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
import json
|
||||
import frappe.utils
|
||||
from frappe.utils import cstr, flt, getdate, comma_and, cint, nowdate, add_days
|
||||
from frappe.utils import cstr, flt, getdate, cint, nowdate, add_days, get_link_to_form
|
||||
from frappe import _
|
||||
from six import string_types
|
||||
from frappe.model.utils import get_fetch_values
|
||||
@ -220,8 +220,9 @@ class SalesOrder(SellingController):
|
||||
where t1.name = t2.parent and t2.against_sales_order = %s and t1.docstatus = 1""", self.name)
|
||||
|
||||
if submit_dn:
|
||||
submit_dn = [get_link_to_form("Delivery Note", dn) for dn in submit_dn]
|
||||
frappe.throw(_("Delivery Notes {0} must be cancelled before cancelling this Sales Order")
|
||||
.format(comma_and(submit_dn)))
|
||||
.format(", ".join(submit_dn)))
|
||||
|
||||
# Checks Sales Invoice
|
||||
submit_rv = frappe.db.sql_list("""select t1.name
|
||||
@ -230,8 +231,9 @@ class SalesOrder(SellingController):
|
||||
self.name)
|
||||
|
||||
if submit_rv:
|
||||
submit_rv = [get_link_to_form("Sales Invoice", si) for si in submit_rv]
|
||||
frappe.throw(_("Sales Invoice {0} must be cancelled before cancelling this Sales Order")
|
||||
.format(comma_and(submit_rv)))
|
||||
.format(", ".join(submit_rv)))
|
||||
|
||||
#check maintenance schedule
|
||||
submit_ms = frappe.db.sql_list("""
|
||||
@ -240,8 +242,9 @@ class SalesOrder(SellingController):
|
||||
where t2.parent=t1.name and t2.sales_order = %s and t1.docstatus = 1""", self.name)
|
||||
|
||||
if submit_ms:
|
||||
submit_ms = [get_link_to_form("Maintenance Schedule", ms) for ms in submit_ms]
|
||||
frappe.throw(_("Maintenance Schedule {0} must be cancelled before cancelling this Sales Order")
|
||||
.format(comma_and(submit_ms)))
|
||||
.format(", ".join(submit_ms)))
|
||||
|
||||
# check maintenance visit
|
||||
submit_mv = frappe.db.sql_list("""
|
||||
@ -250,8 +253,9 @@ class SalesOrder(SellingController):
|
||||
where t2.parent=t1.name and t2.prevdoc_docname = %s and t1.docstatus = 1""",self.name)
|
||||
|
||||
if submit_mv:
|
||||
submit_mv = [get_link_to_form("Maintenance Visit", mv) for mv in submit_mv]
|
||||
frappe.throw(_("Maintenance Visit {0} must be cancelled before cancelling this Sales Order")
|
||||
.format(comma_and(submit_mv)))
|
||||
.format(", ".join(submit_mv)))
|
||||
|
||||
# check work order
|
||||
pro_order = frappe.db.sql_list("""
|
||||
@ -260,8 +264,9 @@ class SalesOrder(SellingController):
|
||||
where sales_order = %s and docstatus = 1""", self.name)
|
||||
|
||||
if pro_order:
|
||||
pro_order = [get_link_to_form("Work Order", po) for po in pro_order]
|
||||
frappe.throw(_("Work Order {0} must be cancelled before cancelling this Sales Order")
|
||||
.format(comma_and(pro_order)))
|
||||
.format(", ".join(pro_order)))
|
||||
|
||||
def check_modified_date(self):
|
||||
mod_db = frappe.db.get_value("Sales Order", self.name, "modified")
|
||||
@ -349,15 +354,7 @@ class SalesOrder(SellingController):
|
||||
|
||||
def set_indicator(self):
|
||||
"""Set indicator for portal"""
|
||||
if self.status == 'Closed':
|
||||
self.indicator_color = "green"
|
||||
self.indicator_title = _("Closed")
|
||||
|
||||
elif self.per_delivered < 100 and getdate(self.delivery_date) < getdate(nowdate()):
|
||||
self.indicator_color = "red"
|
||||
self.indicator_title = _("Overdue")
|
||||
|
||||
elif self.per_billed < 100 and self.per_delivered < 100:
|
||||
if self.per_billed < 100 and self.per_delivered < 100:
|
||||
self.indicator_color = "orange"
|
||||
self.indicator_title = _("Not Paid and Not Delivered")
|
||||
|
||||
@ -951,4 +948,4 @@ def make_raw_material_request(items, company, sales_order, project=None):
|
||||
material_request.flags.ignore_permissions = 1
|
||||
material_request.run_method("set_missing_values")
|
||||
material_request.submit()
|
||||
return material_request
|
||||
return material_request
|
||||
|
@ -12,6 +12,8 @@ from erpnext.selling.doctype.sales_order.sales_order import make_work_orders
|
||||
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")
|
||||
@ -268,6 +270,22 @@ class TestSalesOrder(unittest.TestCase):
|
||||
self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"),
|
||||
existing_reserved_qty_item2 + 20)
|
||||
|
||||
def test_add_new_item_in_update_child_qty_rate(self):
|
||||
so = make_sales_order(item_code= "_Test Item", qty=4)
|
||||
create_dn_against_so(so.name, 4)
|
||||
make_sales_invoice(so.name)
|
||||
|
||||
trans_item = json.dumps([{'item_code' : '_Test Item 2', 'rate' : 200, 'qty' : 7}])
|
||||
update_child_qty_rate('Sales Order', trans_item, so.name)
|
||||
|
||||
so.reload()
|
||||
self.assertEqual(so.get("items")[-1].item_code, '_Test Item 2')
|
||||
self.assertEqual(so.get("items")[-1].rate, 200)
|
||||
self.assertEqual(so.get("items")[-1].qty, 7)
|
||||
self.assertEqual(so.get("items")[-1].amount, 1400)
|
||||
self.assertEqual(so.status, 'To Deliver and Bill')
|
||||
|
||||
|
||||
def test_update_child_qty_rate(self):
|
||||
so = make_sales_order(item_code= "_Test Item", qty=4)
|
||||
create_dn_against_so(so.name, 4)
|
||||
@ -760,7 +778,7 @@ def make_sales_order(**args):
|
||||
})
|
||||
|
||||
so.delivery_date = add_days(so.transaction_date, 10)
|
||||
|
||||
|
||||
if not args.do_not_save:
|
||||
so.insert()
|
||||
if not args.do_not_submit:
|
||||
|
@ -49,11 +49,15 @@ def get_items(start, page_length, price_list, item_group, search_value="", pos_p
|
||||
where
|
||||
i.disabled = 0 and i.has_variants = 0 and i.is_sales_item = 1
|
||||
and i.item_group in (select name from `tabItem Group` where lft >= {lft} and rgt <= {rgt})
|
||||
and {condition} limit {start}, {page_length}""".format(start=start,page_length=page_length,lft=lft, rgt=rgt, condition=condition),
|
||||
{
|
||||
'item_code': item_code,
|
||||
and {condition} limit {start}, {page_length}""".format(
|
||||
start=start,
|
||||
page_length=page_length,
|
||||
lft=lft,
|
||||
rgt=rgt,
|
||||
condition=condition
|
||||
), {
|
||||
'price_list': price_list
|
||||
} , as_dict=1)
|
||||
}, as_dict=1)
|
||||
|
||||
res = {
|
||||
'items': res
|
||||
@ -134,7 +138,7 @@ def get_conditions(item_code, serial_no, batch_no, barcode):
|
||||
condition = """(i.name like %(item_code)s
|
||||
or i.item_name like %(item_code)s)"""
|
||||
|
||||
return '%%%s%%'%(frappe.db.escape(item_code)), condition
|
||||
return frappe.db.escape('%' + item_code + '%'), condition
|
||||
|
||||
def get_item_group_condition(pos_profile):
|
||||
cond = "and 1=1"
|
||||
|
@ -29,9 +29,11 @@ def execute(filters=None):
|
||||
|
||||
if customer_naming_type == "Naming Series":
|
||||
row = [d.name, d.customer_name, credit_limit, outstanding_amt, bal,
|
||||
d.bypass_credit_limit_check_at_sales_order, d.disabled]
|
||||
d.bypass_credit_limit_check_at_sales_order, d.is_frozen,
|
||||
d.disabled]
|
||||
else:
|
||||
row = [d.name, credit_limit, outstanding_amt, bal, d.bypass_credit_limit_check_at_sales_order, d.disabled]
|
||||
row = [d.name, credit_limit, outstanding_amt, bal,
|
||||
d.bypass_credit_limit_check_at_sales_order, d.is_frozen, d.disabled]
|
||||
|
||||
if credit_limit:
|
||||
data.append(row)
|
||||
@ -44,8 +46,9 @@ def get_columns(customer_naming_type):
|
||||
_("Credit Limit") + ":Currency:120",
|
||||
_("Outstanding Amt") + ":Currency:100",
|
||||
_("Credit Balance") + ":Currency:120",
|
||||
_("Bypass credit check at Sales Order ") + ":Check:240",
|
||||
_("Is Disabled ") + ":Check:240"
|
||||
_("Bypass credit check at Sales Order ") + ":Check:80",
|
||||
_("Is Frozen") + ":Check:80",
|
||||
_("Disabled") + ":Check:80",
|
||||
]
|
||||
|
||||
if customer_naming_type == "Naming Series":
|
||||
@ -60,5 +63,5 @@ def get_details(filters):
|
||||
conditions += " where name = %(customer)s"
|
||||
|
||||
return frappe.db.sql("""select name, customer_name,
|
||||
bypass_credit_limit_check_at_sales_order,disabled from `tabCustomer` %s
|
||||
""" % conditions, filters, as_dict=1)
|
||||
bypass_credit_limit_check_at_sales_order, is_frozen, disabled from `tabCustomer` %s
|
||||
""" % conditions, filters, as_dict=1)
|
||||
|
@ -76,10 +76,21 @@ frappe.query_reports["Sales Analytics"] = {
|
||||
events: {
|
||||
onCheckRow: function(data) {
|
||||
row_name = data[2].content;
|
||||
length = data.length
|
||||
row_values = data.slice(4,length-1).map(function (column) {
|
||||
return column.content;
|
||||
})
|
||||
length = data.length;
|
||||
|
||||
var tree_type = frappe.query_report.filters[0].value;
|
||||
|
||||
if(tree_type == "Customer" || tree_type == "Item") {
|
||||
row_values = data.slice(4,length-1).map(function (column) {
|
||||
return column.content;
|
||||
})
|
||||
}
|
||||
else {
|
||||
row_values = data.slice(3,length-1).map(function (column) {
|
||||
return column.content;
|
||||
})
|
||||
}
|
||||
|
||||
entry = {
|
||||
'name':row_name,
|
||||
'values':row_values
|
||||
|
@ -276,7 +276,11 @@ class Analytics(object):
|
||||
|
||||
def get_chart_data(self):
|
||||
length = len(self.columns)
|
||||
labels = [d.get("label") for d in self.columns[2:length-1]]
|
||||
|
||||
if self.filters.tree_type in ["Customer", "Supplier", "Item"]:
|
||||
labels = [d.get("label") for d in self.columns[2:length-1]]
|
||||
else:
|
||||
labels = [d.get("label") for d in self.columns[1:length-1]]
|
||||
self.chart = {
|
||||
"data": {
|
||||
'labels': labels,
|
||||
|
@ -40,7 +40,7 @@ class AuthorizationControl(TransactionBase):
|
||||
chk = 1
|
||||
add_cond1,add_cond2 = '',''
|
||||
if based_on == 'Itemwise Discount':
|
||||
add_cond1 += " and master_name = '"+frappe.db.escape(cstr(item))+"'"
|
||||
add_cond1 += " and master_name = " + frappe.db.escape(cstr(item))
|
||||
itemwise_exists = frappe.db.sql("""select value from `tabAuthorization Rule`
|
||||
where transaction = %s and value <= %s
|
||||
and based_on = %s and company = %s and docstatus != 2 %s %s""" %
|
||||
|
@ -389,17 +389,19 @@ def update_company_current_month_sales(company):
|
||||
current_month_year = formatdate(today(), "MM-yyyy")
|
||||
|
||||
results = frappe.db.sql('''
|
||||
select
|
||||
sum(base_grand_total) as total, date_format(posting_date, '%m-%Y') as month_year
|
||||
from
|
||||
SELECT
|
||||
SUM(base_grand_total) AS total,
|
||||
DATE_FORMAT(`posting_date`, '%m-%Y') AS month_year
|
||||
FROM
|
||||
`tabSales Invoice`
|
||||
where
|
||||
date_format(posting_date, '%m-%Y')="{0}"
|
||||
and docstatus = 1
|
||||
and company = "{1}"
|
||||
group by
|
||||
WHERE
|
||||
DATE_FORMAT(`posting_date`, '%m-%Y') = '{current_month_year}'
|
||||
AND docstatus = 1
|
||||
AND company = {company}
|
||||
GROUP BY
|
||||
month_year
|
||||
'''.format(current_month_year, frappe.db.escape(company)), as_dict = True)
|
||||
'''.format(current_month_year=current_month_year, company=frappe.db.escape(company)),
|
||||
as_dict = True)
|
||||
|
||||
monthly_total = results[0]['total'] if len(results) > 0 else 0
|
||||
|
||||
@ -409,7 +411,7 @@ def update_company_monthly_sales(company):
|
||||
'''Cache past year monthly sales of every company based on sales invoices'''
|
||||
from frappe.utils.goal import get_monthly_results
|
||||
import json
|
||||
filter_str = "company = '{0}' and status != 'Draft' and docstatus=1".format(frappe.db.escape(company))
|
||||
filter_str = "company = {0} and status != 'Draft' and docstatus=1".format(frappe.db.escape(company))
|
||||
month_to_value_dict = get_monthly_results("Sales Invoice", "base_grand_total",
|
||||
"posting_date", filter_str, "sum")
|
||||
|
||||
@ -441,9 +443,9 @@ def get_children(doctype, parent=None, company=None, is_root=False):
|
||||
from
|
||||
`tab{doctype}` comp
|
||||
where
|
||||
ifnull(parent_company, "")="{parent}"
|
||||
ifnull(parent_company, "")={parent}
|
||||
""".format(
|
||||
doctype = frappe.db.escape(doctype),
|
||||
doctype = doctype,
|
||||
parent=frappe.db.escape(parent)
|
||||
), as_dict=1)
|
||||
|
||||
|
@ -89,18 +89,18 @@ def delete_lead_addresses(company_name):
|
||||
leads = [ "'%s'"%row.get("name") for row in leads ]
|
||||
addresses = []
|
||||
if leads:
|
||||
addresses = frappe.db.sql_list("""select parent from `tabDynamic Link` where link_name
|
||||
addresses = frappe.db.sql_list("""select parent from `tabDynamic Link` where link_name
|
||||
in ({leads})""".format(leads=",".join(leads)))
|
||||
|
||||
if addresses:
|
||||
addresses = ["'%s'"%frappe.db.escape(addr) for addr in addresses]
|
||||
addresses = ["%s" % frappe.db.escape(addr) for addr in addresses]
|
||||
|
||||
frappe.db.sql("""delete from tabAddress where name in ({addresses}) and
|
||||
name not in (select distinct dl1.parent from `tabDynamic Link` dl1
|
||||
inner join `tabDynamic Link` dl2 on dl1.parent=dl2.parent
|
||||
frappe.db.sql("""delete from tabAddress where name in ({addresses}) and
|
||||
name not in (select distinct dl1.parent from `tabDynamic Link` dl1
|
||||
inner join `tabDynamic Link` dl2 on dl1.parent=dl2.parent
|
||||
and dl1.link_doctype<>dl2.link_doctype)""".format(addresses=",".join(addresses)))
|
||||
|
||||
frappe.db.sql("""delete from `tabDynamic Link` where link_doctype='Lead'
|
||||
frappe.db.sql("""delete from `tabDynamic Link` where link_doctype='Lead'
|
||||
and parenttype='Address' and link_name in ({leads})""".format(leads=",".join(leads)))
|
||||
|
||||
frappe.db.sql("""update tabCustomer set lead_name=NULL where lead_name in ({leads})""".format(leads=",".join(leads)))
|
||||
|
@ -4,7 +4,7 @@
|
||||
"allow_rename": 0,
|
||||
"autoname": "Prompt",
|
||||
"beta": 0,
|
||||
"creation": "2013-02-21 14:15:31",
|
||||
"creation": "2018-09-16 22:00:00",
|
||||
"custom": 0,
|
||||
"description": "Send regular summary reports via Email.",
|
||||
"docstatus": 0,
|
||||
@ -563,6 +563,90 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "work_in_progress",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Work in Progress",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "sales_orders_to_bill",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Sales Orders to Bill",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "purchase_orders_to_bill",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Purchase Orders to Bill",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
@ -646,34 +730,6 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "pending_sales_orders",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Pending Sales Orders",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
@ -686,7 +742,7 @@
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "New Purchase Orders",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
@ -707,15 +763,15 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "pending_purchase_orders",
|
||||
"fieldname": "sales_orders_to_deliver",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Pending Purchase Orders",
|
||||
"in_standard_filter": 0,
|
||||
"label": "Sales Orders to Deliver",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
@ -730,6 +786,34 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "purchase_orders_to_receive",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Purchase Orders to Receive",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
@ -798,8 +882,8 @@
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Pending Quotations",
|
||||
"in_standard_filter": 0,
|
||||
"label": "Open Quotations",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
@ -869,6 +953,34 @@
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "purchase_orders_items_overdue",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Purchase Orders Items Overdue",
|
||||
"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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
@ -1079,7 +1191,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2016-11-07 05:10:32.190134",
|
||||
"modified": "2018-09-16 22:00:00.000000",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Email Digest",
|
||||
|
@ -96,7 +96,13 @@ class EmailDigest(Document):
|
||||
quote = get_random_quote()
|
||||
context.quote = {"text": quote[0], "author": quote[1]}
|
||||
|
||||
if not (context.events or context.todo_list or context.notifications or context.cards):
|
||||
if self.get("purchase_orders_items_overdue"):
|
||||
context.purchase_order_list, context.purchase_orders_items_overdue_list = self.get_purchase_orders_items_overdue_list()
|
||||
if not context.purchase_order_list:
|
||||
frappe.throw(_("No items to be received are overdue"))
|
||||
|
||||
if not (context.events or context.todo_list or context.notifications or context.cards
|
||||
or context.purchase_orders_items_overdue_list):
|
||||
return None
|
||||
|
||||
frappe.flags.ignore_account_permission = False
|
||||
@ -230,9 +236,10 @@ class EmailDigest(Document):
|
||||
|
||||
cache = frappe.cache()
|
||||
context.cards = []
|
||||
for key in ("income", "expenses_booked", "income_year_to_date","expense_year_to_date",
|
||||
"new_quotations","pending_quotations","sales_order","purchase_order","pending_sales_orders","pending_purchase_orders",
|
||||
"invoiced_amount", "payables", "bank_balance", "credit_balance"):
|
||||
for key in ("income", "expenses_booked", "income_year_to_date", "expense_year_to_date",
|
||||
"bank_balance", "credit_balance", "invoiced_amount", "payables",
|
||||
"sales_orders_to_bill", "purchase_orders_to_bill", "sales_order", "purchase_order",
|
||||
"sales_orders_to_deliver", "purchase_orders_to_receive", "new_quotations", "pending_quotations"):
|
||||
if self.get(key):
|
||||
cache_key = "email_digest:card:{0}:{1}:{2}:{3}".format(self.company, self.frequency, key, self.from_date)
|
||||
card = cache.get(cache_key)
|
||||
@ -346,6 +353,62 @@ class EmailDigest(Document):
|
||||
|
||||
return balance, past_balance, count
|
||||
|
||||
def get_sales_orders_to_bill(self):
|
||||
"""Get value not billed"""
|
||||
|
||||
value, count = frappe.db.sql("""select ifnull((sum(grand_total)) - (sum(grand_total*per_billed/100)),0),
|
||||
count(*) from `tabSales Order`
|
||||
where (transaction_date <= %(to_date)s) and billing_status != "Fully Billed"
|
||||
and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date})[0]
|
||||
|
||||
return {
|
||||
"label": self.meta.get_label("sales_orders_to_bill"),
|
||||
"value": value,
|
||||
"count": count
|
||||
}
|
||||
|
||||
def get_sales_orders_to_deliver(self):
|
||||
"""Get value not delivered"""
|
||||
|
||||
value, count = frappe.db.sql("""select ifnull((sum(grand_total)) - (sum(grand_total*per_delivered/100)),0),
|
||||
count(*) from `tabSales Order`
|
||||
where (transaction_date <= %(to_date)s) and delivery_status != "Fully Delivered"
|
||||
and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date})[0]
|
||||
|
||||
return {
|
||||
"label": self.meta.get_label("sales_orders_to_deliver"),
|
||||
"value": value,
|
||||
"count": count
|
||||
}
|
||||
|
||||
def get_purchase_orders_to_receive(self):
|
||||
"""Get value not received"""
|
||||
|
||||
value, count = frappe.db.sql("""select ifnull((sum(grand_total))-(sum(grand_total*per_received/100)),0),
|
||||
count(*) from `tabPurchase Order`
|
||||
where (transaction_date <= %(to_date)s) and per_received < 100
|
||||
and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date})[0]
|
||||
|
||||
return {
|
||||
"label": self.meta.get_label("purchase_orders_to_receive"),
|
||||
"value": value,
|
||||
"count": count
|
||||
}
|
||||
|
||||
def get_purchase_orders_to_bill(self):
|
||||
"""Get purchase not billed"""
|
||||
|
||||
value, count = frappe.db.sql("""select ifnull((sum(grand_total)) - (sum(grand_total*per_billed/100)),0),
|
||||
count(*) from `tabPurchase Order`
|
||||
where (transaction_date <= %(to_date)s) and per_billed < 100
|
||||
and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date})[0]
|
||||
|
||||
return {
|
||||
"label": self.meta.get_label("purchase_orders_to_bill"),
|
||||
"value": value,
|
||||
"count": count
|
||||
}
|
||||
|
||||
def get_type_balance(self, fieldname, account_type, root_type=None):
|
||||
|
||||
if root_type:
|
||||
@ -529,6 +592,30 @@ class EmailDigest(Document):
|
||||
else:
|
||||
return fmt_money(value, currency=self.currency)
|
||||
|
||||
def get_purchase_orders_items_overdue_list(self):
|
||||
fields_po = "distinct `tabPurchase Order Item`.parent as po"
|
||||
fields_poi = "`tabPurchase Order Item`.parent, `tabPurchase Order Item`.schedule_date, item_code," \
|
||||
"received_qty, qty - received_qty as missing_qty, rate, amount"
|
||||
|
||||
sql_po = """select {fields} from `tabPurchase Order Item`
|
||||
left join `tabPurchase Order` on `tabPurchase Order`.name = `tabPurchase Order Item`.parent
|
||||
where status<>'Closed' and `tabPurchase Order Item`.docstatus=1 and curdate() > `tabPurchase Order Item`.schedule_date
|
||||
and received_qty < qty order by `tabPurchase Order Item`.parent DESC,
|
||||
`tabPurchase Order Item`.schedule_date DESC""".format(fields=fields_po)
|
||||
|
||||
sql_poi = """select {fields} from `tabPurchase Order Item`
|
||||
left join `tabPurchase Order` on `tabPurchase Order`.name = `tabPurchase Order Item`.parent
|
||||
where status<>'Closed' and `tabPurchase Order Item`.docstatus=1 and curdate() > `tabPurchase Order Item`.schedule_date
|
||||
and received_qty < qty order by `tabPurchase Order Item`.idx""".format(fields=fields_poi)
|
||||
purchase_order_list = frappe.db.sql(sql_po, as_dict=True)
|
||||
purchase_order_items_overdue_list = frappe.db.sql(sql_poi, as_dict=True)
|
||||
|
||||
for t in purchase_order_items_overdue_list:
|
||||
t.link = get_url_to_form("Purchase Order", t.parent)
|
||||
t.rate = fmt_money(t.rate, 2, t.currency)
|
||||
t.amount = fmt_money(t.amount, 2, t.currency)
|
||||
return purchase_order_list, purchase_order_items_overdue_list
|
||||
|
||||
def send():
|
||||
now_date = now_datetime().date()
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{% macro show_card(card) %}
|
||||
<div style="width: 50%; float:left; min-height: 80px; padding-top: 20px;">
|
||||
<h6 style="color: {{ text_muted }}; font-size: 12px; margin-bottom: 0px; margin-top: 0px;">{{ _(card.label) }}
|
||||
<div style="width: 49%; display:inline-block; vertical-align: top; min-height: 80px; padding-top: 20px;">
|
||||
<h6 style="color: {{ text_muted }}; font-size: 12px; margin-bottom: 0px; margin-top: 0px;">{{ card.label }}
|
||||
{% if card.count %}
|
||||
<span class="badge">({{ card.count }})</span>
|
||||
{% endif %}</h6>
|
||||
@ -180,5 +180,80 @@
|
||||
<br>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
<!-- Purchase Order Items Overdue -->
|
||||
{% if purchase_orders_items_overdue_list %}
|
||||
<h4 style="{{ section_head }}" class="text-center">{{ _("Purchase Order Items not received on time") }}</h4>
|
||||
<div>
|
||||
<div style="background-color: #fafbfc;">
|
||||
<hr>
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<th style="width: 40%;">
|
||||
<span style="padding: 3px 7px; margin-right: 7px; font-weight: bold; {{ link_css }}">Item Code</span>
|
||||
</th>
|
||||
<th style="width: 20%; text-align: right">
|
||||
<span style="padding: 3px 7px; margin-right: 7px; font-weight: bold; {{ link_css }}">Quantity</span>
|
||||
</th>
|
||||
<th style="width: 20%; text-align: right">
|
||||
<span style="padding: 3px 7px; margin-right: 7px; font-weight: bold; {{ link_css }}">Rate</span>
|
||||
</th>
|
||||
<th style="width: 20%; text-align: right">
|
||||
<span style="padding: 3px 7px; margin-right: 7px; font-weight: bold; {{ link_css }}">Amount</span>
|
||||
</th>
|
||||
</tr>
|
||||
</table>
|
||||
<hr>
|
||||
</div>
|
||||
<div>
|
||||
{% for po in purchase_order_list %}
|
||||
<div style="{{ line_item }}">
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<th>
|
||||
<span style="padding: 3px 7px; margin-right: 7px; font-weight: bold;">{{ po.po }}</span>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
{% for t in purchase_orders_items_overdue_list %}
|
||||
{% if t.parent == po.po %}
|
||||
<div >
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<td style="padding-left: 7px;">
|
||||
<a style="width: 40%; {{ link_css }}" href="{{ t.link }}">{{ _(t.item_code) }}</a>
|
||||
</td>
|
||||
<td style="width: 20%; text-align: right">
|
||||
<span style="{{ label_css }}">
|
||||
{{ t.missing_qty }}
|
||||
</span>
|
||||
</td>
|
||||
<td style="width: 20%; text-align: right">
|
||||
<span style="{{ label_css }}">
|
||||
{{ t.rate }}
|
||||
</span>
|
||||
</td>
|
||||
<td style="width: 20%; text-align: right">
|
||||
<span style="{{ label_css }}">
|
||||
{{ t.amount }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<br><br><span class="text-danger">Please take necessary action</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
@ -88,7 +88,7 @@ def get_product_list_for_group(product_group=None, start=0, limit=10, search=Non
|
||||
# return child item groups if the type is of "Is Group"
|
||||
return get_child_groups_for_list_in_html(item_group, start, limit, search)
|
||||
|
||||
child_groups = ", ".join(['"' + frappe.db.escape(i[0]) + '"' for i in get_child_groups(product_group)])
|
||||
child_groups = ", ".join([frappe.db.escape(i[0]) for i in get_child_groups(product_group)])
|
||||
|
||||
# base query
|
||||
query = """select I.name, I.item_name, I.item_code, I.route, I.image, I.website_image, I.thumbnail, I.item_group,
|
||||
|
@ -20,6 +20,6 @@ def get_party_type(doctype, txt, searchfield, start, page_len, filters):
|
||||
where `{key}` LIKE %(txt)s {cond}
|
||||
order by name limit %(start)s, %(page_len)s"""
|
||||
.format(key=searchfield, cond=cond), {
|
||||
'txt': "%%%s%%" % frappe.db.escape(txt),
|
||||
'txt': '%' + txt + '%',
|
||||
'start': start, 'page_len': page_len
|
||||
})
|
||||
|
@ -5,7 +5,6 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import cstr, getdate
|
||||
from frappe.utils.file_manager import save_file
|
||||
from .default_website import website_maker
|
||||
from erpnext.accounts.doctype.account.account import RootNotEditable
|
||||
|
||||
@ -107,10 +106,16 @@ def create_logo(args):
|
||||
attach_logo = args.get("attach_logo").split(",")
|
||||
if len(attach_logo)==3:
|
||||
filename, filetype, content = attach_logo
|
||||
fileurl = save_file(filename, content, "Website Settings", "Website Settings",
|
||||
decode=True).file_url
|
||||
_file = frappe.get_doc({
|
||||
"doctype": "File",
|
||||
"file_name": filename,
|
||||
"attached_to_doctype": "Website Settings",
|
||||
"attached_to_name": "Website Settings",
|
||||
"decode": True})
|
||||
_file.save()
|
||||
fileurl = _file.file_url
|
||||
frappe.db.set_value("Website Settings", "Website Settings", "brand_html",
|
||||
"<img src='{0}' style='max-width: 40px; max-height: 25px;'> {1}".format(fileurl, args.get("company_name") ))
|
||||
"<img src='{0}' style='max-width: 40px; max-height: 25px;'> {1}".format(fileurl, args.get("company_name")))
|
||||
|
||||
def create_website(args):
|
||||
website_maker(args)
|
||||
@ -121,4 +126,4 @@ def get_fy_details(fy_start_date, fy_end_date):
|
||||
fy = cstr(start_year)
|
||||
else:
|
||||
fy = cstr(start_year) + '-' + cstr(start_year + 1)
|
||||
return fy
|
||||
return fy
|
||||
|
@ -28,9 +28,10 @@ class ShoppingCartSettings(Document):
|
||||
raise_exception=ShoppingCartSetupError)
|
||||
|
||||
price_list_currency_map = frappe.db.get_values("Price List",
|
||||
[self.price_list],
|
||||
"currency")
|
||||
[self.price_list], "currency")
|
||||
|
||||
price_list_currency_map = dict(price_list_currency_map)
|
||||
|
||||
# check if all price lists have a currency
|
||||
for price_list, currency in price_list_currency_map.items():
|
||||
if not currency:
|
||||
@ -39,8 +40,8 @@ class ShoppingCartSettings(Document):
|
||||
expected_to_exist = [currency + "-" + company_currency
|
||||
for currency in price_list_currency_map.values()
|
||||
if currency != company_currency]
|
||||
|
||||
# manqala 20/09/2016: set up selection parameters for query from tabCurrency Exchange
|
||||
|
||||
# manqala 20/09/2016: set up selection parameters for query from tabCurrency Exchange
|
||||
from_currency = [currency for currency in price_list_currency_map.values() if currency != company_currency]
|
||||
to_currency = company_currency
|
||||
# manqala end
|
||||
|
@ -26,11 +26,12 @@ def boot_session(bootinfo):
|
||||
'default_valid_till'))
|
||||
|
||||
# if no company, show a dialog box to create a new company
|
||||
bootinfo.customer_count = frappe.db.sql("""select count(*) from tabCustomer""")[0][0]
|
||||
bootinfo.customer_count = frappe.db.sql("""SELECT count(*) FROM `tabCustomer`""")[0][0]
|
||||
|
||||
if not bootinfo.customer_count:
|
||||
bootinfo.setup_complete = frappe.db.sql("""select name from
|
||||
tabCompany limit 1""") and 'Yes' or 'No'
|
||||
bootinfo.setup_complete = frappe.db.sql("""SELECT `name`
|
||||
FROM `tabCompany`
|
||||
LIMIT 1""") and 'Yes' or 'No'
|
||||
|
||||
bootinfo.docs += frappe.db.sql("""select name, default_currency, cost_center, default_terms,
|
||||
default_letter_head, default_bank_account, enable_perpetual_inventory from `tabCompany`""",
|
||||
|
@ -177,6 +177,9 @@ class DeliveryNote(SellingController):
|
||||
frappe.msgprint(_("Note: Item {0} entered multiple times").format(d.item_code))
|
||||
else:
|
||||
chk_dupl_itm.append(f)
|
||||
#Customer Provided parts will have zero valuation rate
|
||||
if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'):
|
||||
d.allow_zero_valuation_rate = 1
|
||||
|
||||
def validate_warehouse(self):
|
||||
super(DeliveryNote, self).validate_warehouse()
|
||||
|
@ -115,6 +115,8 @@ frappe.ui.form.on("Item", {
|
||||
['is_stock_item', 'has_serial_no', 'has_batch_no'].forEach((fieldname) => {
|
||||
frm.set_df_property(fieldname, 'read_only', stock_exists);
|
||||
});
|
||||
|
||||
frm.toggle_reqd('customer', frm.doc.is_customer_provided_item ? 1:0);
|
||||
},
|
||||
|
||||
validate: function(frm){
|
||||
@ -124,6 +126,10 @@ frappe.ui.form.on("Item", {
|
||||
image: function() {
|
||||
refresh_field("image_view");
|
||||
},
|
||||
|
||||
is_customer_provided_item: function(frm) {
|
||||
frm.toggle_reqd('customer', frm.doc.is_customer_provided_item ? 1:0);
|
||||
},
|
||||
|
||||
is_fixed_asset: function(frm) {
|
||||
frm.call({
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -128,6 +128,7 @@ class Item(WebsiteGenerator):
|
||||
self.validate_uom_conversion_factor()
|
||||
self.validate_item_defaults()
|
||||
self.update_defaults_from_item_group()
|
||||
self.validate_customer_provided_part()
|
||||
|
||||
if not self.get("__islocal"):
|
||||
self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group")
|
||||
@ -147,6 +148,14 @@ class Item(WebsiteGenerator):
|
||||
if cint(frappe.db.get_single_value('Stock Settings', 'clean_description_html')):
|
||||
self.description = clean_html(self.description)
|
||||
|
||||
def validate_customer_provided_part(self):
|
||||
if self.is_customer_provided_item:
|
||||
if self.is_purchase_item:
|
||||
frappe.throw(_('"Customer Provided Item" cannot be Purchase Item also'))
|
||||
if self.valuation_rate:
|
||||
frappe.throw(_('"Customer Provided Item" cannot have Valuation Rate'))
|
||||
self.default_material_request_type = "Customer Provided"
|
||||
|
||||
def add_price(self, price_list=None):
|
||||
'''Add a new price'''
|
||||
if not price_list:
|
||||
@ -261,7 +270,7 @@ class Item(WebsiteGenerator):
|
||||
"file_url": self.website_image,
|
||||
"attached_to_doctype": "Item",
|
||||
"attached_to_name": self.name
|
||||
}).insert()
|
||||
}).save()
|
||||
|
||||
except IOError:
|
||||
self.website_image = None
|
||||
@ -972,7 +981,7 @@ def get_uom_conv_factor(uom, stock_uom):
|
||||
value = ""
|
||||
uom_details = frappe.db.sql("""select to_uom, from_uom, value from `tabUOM Conversion Factor`\
|
||||
where to_uom in ({0})
|
||||
""".format(', '.join(['"' + frappe.db.escape(i, percent=False) + '"' for i in uoms])), as_dict=True)
|
||||
""".format(', '.join([frappe.db.escape(i, percent=False) for i in uoms])), as_dict=True)
|
||||
|
||||
for d in uom_details:
|
||||
if d.from_uom == stock_uom and d.to_uom == uom:
|
||||
|
@ -378,7 +378,7 @@ def make_item_variant():
|
||||
|
||||
test_records = frappe.get_test_records('Item')
|
||||
|
||||
def create_item(item_code, is_stock_item=None, valuation_rate=0, warehouse=None):
|
||||
def create_item(item_code, is_stock_item=None, valuation_rate=0, warehouse=None, is_customer_provided_item=None, customer=None, is_purchase_item=None):
|
||||
if not frappe.db.exists("Item", item_code):
|
||||
item = frappe.new_doc("Item")
|
||||
item.item_code = item_code
|
||||
@ -387,6 +387,9 @@ def create_item(item_code, is_stock_item=None, valuation_rate=0, warehouse=None)
|
||||
item.item_group = "All Item Groups"
|
||||
item.is_stock_item = is_stock_item or 1
|
||||
item.valuation_rate = valuation_rate or 0.0
|
||||
item.is_purchase_item = is_purchase_item
|
||||
item.is_customer_provided_item = is_customer_provided_item
|
||||
item.customer = customer or ''
|
||||
item.append("item_defaults", {
|
||||
"default_warehouse": warehouse or '_Test Warehouse - _TC',
|
||||
"company": "_Test Company"
|
||||
|
@ -35,6 +35,6 @@ def get_alternative_items(doctype, txt, searchfield, start, page_len, filters):
|
||||
where alternative_item_code = %(item_code)s and item_code like %(txt)s
|
||||
and two_way = 1) limit {0}, {1}
|
||||
""".format(start, page_len), {
|
||||
"item_code": frappe.db.escape(filters.get('item_code')),
|
||||
"txt": "%%%s%%" % frappe.db.escape(txt)
|
||||
"item_code": filters.get('item_code'),
|
||||
"txt": '%' + txt + '%'
|
||||
})
|
@ -40,6 +40,7 @@ frappe.ui.form.on('Material Request', {
|
||||
|
||||
refresh: function(frm) {
|
||||
frm.events.make_custom_buttons(frm);
|
||||
frm.toggle_reqd('customer', frm.doc.material_request_type=="Customer Provided");
|
||||
},
|
||||
|
||||
make_custom_buttons: function(frm) {
|
||||
@ -61,6 +62,11 @@ frappe.ui.form.on('Material Request', {
|
||||
() => frm.events.make_stock_entry(frm), __('Create'));
|
||||
}
|
||||
|
||||
if (frm.doc.material_request_type === "Customer Provided") {
|
||||
frm.add_custom_button(__("Material Receipt"),
|
||||
() => frm.events.make_stock_entry(frm), __("Make"));
|
||||
}
|
||||
|
||||
if (frm.doc.material_request_type === "Purchase") {
|
||||
frm.add_custom_button(__('Purchase Order'),
|
||||
() => frm.events.make_purchase_order(frm), __('Create'));
|
||||
@ -259,6 +265,9 @@ frappe.ui.form.on('Material Request', {
|
||||
}
|
||||
});
|
||||
},
|
||||
material_request_type: function(frm) {
|
||||
frm.toggle_reqd('customer', frm.doc.material_request_type=="Customer Provided");
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -70,7 +70,7 @@ class MaterialRequest(BuyingController):
|
||||
from erpnext.controllers.status_updater import validate_status
|
||||
validate_status(self.status,
|
||||
["Draft", "Submitted", "Stopped", "Cancelled", "Pending",
|
||||
"Partially Ordered", "Ordered", "Issued", "Transferred"])
|
||||
"Partially Ordered", "Ordered", "Issued", "Transferred", "Received"])
|
||||
|
||||
validate_for_items(self)
|
||||
|
||||
@ -154,7 +154,7 @@ class MaterialRequest(BuyingController):
|
||||
|
||||
for d in self.get("items"):
|
||||
if d.name in mr_items:
|
||||
if self.material_request_type in ("Material Issue", "Material Transfer"):
|
||||
if self.material_request_type in ("Material Issue", "Material Transfer", "Customer Provided"):
|
||||
d.ordered_qty = flt(frappe.db.sql("""select sum(transfer_qty)
|
||||
from `tabStock Entry Detail` where material_request = %s
|
||||
and material_request_item = %s and docstatus = 1""",
|
||||
@ -239,6 +239,18 @@ def update_item(obj, target, source_parent):
|
||||
target.qty = flt(flt(obj.stock_qty) - flt(obj.ordered_qty))/ target.conversion_factor
|
||||
target.stock_qty = (target.qty * target.conversion_factor)
|
||||
|
||||
def get_list_context(context=None):
|
||||
from erpnext.controllers.website_list_for_contact import get_list_context
|
||||
list_context = get_list_context(context)
|
||||
list_context.update({
|
||||
'show_sidebar': True,
|
||||
'show_search': True,
|
||||
'no_breadcrumbs': True,
|
||||
'title': _('Material Request'),
|
||||
})
|
||||
|
||||
return list_context
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_status(name, status):
|
||||
material_request = frappe.get_doc('Material Request', name)
|
||||
@ -400,7 +412,7 @@ def make_stock_entry(source_name, target_doc=None):
|
||||
target.transfer_qty = qty * obj.conversion_factor
|
||||
target.conversion_factor = obj.conversion_factor
|
||||
|
||||
if source_parent.material_request_type == "Material Transfer":
|
||||
if source_parent.material_request_type == "Material Transfer" or source_parent.material_request_type == "Customer Provided":
|
||||
target.t_warehouse = obj.warehouse
|
||||
else:
|
||||
target.s_warehouse = obj.warehouse
|
||||
@ -410,6 +422,9 @@ def make_stock_entry(source_name, target_doc=None):
|
||||
if source.job_card:
|
||||
target.purpose = 'Material Transfer for Manufacture'
|
||||
|
||||
if source.material_request_type == "Customer Provided":
|
||||
target.purpose = "Material Receipt"
|
||||
|
||||
target.run_method("calculate_rate_and_amount")
|
||||
target.set_job_card_data()
|
||||
|
||||
|
@ -14,6 +14,8 @@ frappe.listview_settings['Material Request'] = {
|
||||
return [__("Transfered"), "green", "per_ordered,=,100"];
|
||||
} else if (doc.material_request_type == "Material Issue") {
|
||||
return [__("Issued"), "green", "per_ordered,=,100"];
|
||||
} else if (doc.material_request_type == "Customer Provided") {
|
||||
return [__("Received"), "green", "per_ordered,=,100"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ from __future__ import unicode_literals
|
||||
import frappe, unittest, erpnext
|
||||
from frappe.utils import flt, today
|
||||
from erpnext.stock.doctype.material_request.material_request import raise_work_orders
|
||||
from erpnext.stock.doctype.item.test_item import create_item
|
||||
|
||||
class TestMaterialRequest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
@ -601,11 +602,25 @@ class TestMaterialRequest(unittest.TestCase):
|
||||
mr = frappe.get_doc("Material Request", mr.name)
|
||||
self.assertEqual(mr.per_ordered, 100)
|
||||
|
||||
def test_customer_provided_parts_mr(self):
|
||||
from erpnext.stock.doctype.material_request.material_request import make_stock_entry
|
||||
create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0)
|
||||
mr = make_material_request(item_code='CUST-0987', material_request_type='Customer Provided')
|
||||
se = make_stock_entry(mr.name)
|
||||
se.insert()
|
||||
se.submit()
|
||||
self.assertEqual(se.get("items")[0].amount, 0)
|
||||
self.assertEqual(se.get("items")[0].material_request, mr.name)
|
||||
mr = frappe.get_doc("Material Request", mr.name)
|
||||
mr.submit()
|
||||
self.assertEqual(mr.per_ordered, 100)
|
||||
|
||||
def make_material_request(**args):
|
||||
args = frappe._dict(args)
|
||||
mr = frappe.new_doc("Material Request")
|
||||
mr.material_request_type = args.material_request_type or "Purchase"
|
||||
mr.company = args.company or "_Test Company"
|
||||
mr.customer = args.customer or '_Test Customer'
|
||||
mr.append("items", {
|
||||
"item_code": args.item_code or "_Test Item",
|
||||
"qty": args.qty or 10,
|
||||
|
@ -171,7 +171,7 @@ class SerialNo(StockController):
|
||||
where fieldname='serial_no' and fieldtype in ('Text', 'Small Text')"""):
|
||||
|
||||
for item in frappe.db.sql("""select name, serial_no from `tab%s`
|
||||
where serial_no like '%%%s%%'""" % (dt[0], frappe.db.escape(old))):
|
||||
where serial_no like %s""" % (dt[0], frappe.db.escape('%' + old + '%'))):
|
||||
|
||||
serial_nos = map(lambda i: new if i.upper()==old.upper() else i, item[1].split('\n'))
|
||||
frappe.db.sql("""update `tab%s` set serial_no = %s
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user