mirror of
https://github.com/frappe/erpnext.git
synced 2026-07-20 03:12:32 +00:00
Compare commits
107 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0be054ae0f | ||
|
|
912cf9d555 | ||
|
|
7fe0a2023a | ||
|
|
cf191c0483 | ||
|
|
bb8d9f2a57 | ||
|
|
fa4577d18a | ||
|
|
993521d365 | ||
|
|
3372b7411d | ||
|
|
5dbfeb8557 | ||
|
|
f5d2337176 | ||
|
|
67aa7b8735 | ||
|
|
aea67c9843 | ||
|
|
f64aaf5e1c | ||
|
|
00f6fd827e | ||
|
|
ea2cdf8c12 | ||
|
|
29729c8d31 | ||
|
|
536e24c6a1 | ||
|
|
f178dc3b26 | ||
|
|
bc2f311a41 | ||
|
|
5c28aba872 | ||
|
|
f800050b71 | ||
|
|
fea996303f | ||
|
|
833e017111 | ||
|
|
fb4f73605a | ||
|
|
94dfcce434 | ||
|
|
18e5bc1976 | ||
|
|
7e54e6acb8 | ||
|
|
0e52951db8 | ||
|
|
1b050e2dfd | ||
|
|
5674e19e71 | ||
|
|
c49735ed15 | ||
|
|
d1c467e49c | ||
|
|
e14df31c05 | ||
|
|
9ff9f21af0 | ||
|
|
eba361b27d | ||
|
|
cc960806a9 | ||
|
|
75c47d64ff | ||
|
|
300307b22f | ||
|
|
43ebcb5876 | ||
|
|
f254d8c5ee | ||
|
|
8811a8c9b6 | ||
|
|
4f1b88ee0f | ||
|
|
51a7aa9c76 | ||
|
|
33ce5ce048 | ||
|
|
b4a570242e | ||
|
|
481977d368 | ||
|
|
479b2bbbd9 | ||
|
|
4faf57869d | ||
|
|
33eb38cc1b | ||
|
|
c6f72f7c90 | ||
|
|
d082a12411 | ||
|
|
096de8a0d5 | ||
|
|
5638d63866 | ||
|
|
a621466219 | ||
|
|
6155114bfd | ||
|
|
2ca33d4f51 | ||
|
|
f1618af76c | ||
|
|
9f7098115b | ||
|
|
55b8b4e374 | ||
|
|
7cb8c51b2d | ||
|
|
311fe5b3dc | ||
|
|
269868130f | ||
|
|
736eed01c2 | ||
|
|
e014b898bd | ||
|
|
1eb31210e0 | ||
|
|
8d72aa99aa | ||
|
|
3a4503b7a1 | ||
|
|
01952bbc15 | ||
|
|
1ca6365dee | ||
|
|
a7a810a25f | ||
|
|
e62fba062e | ||
|
|
60b7acc9b0 | ||
|
|
d7ddb0c14e | ||
|
|
8d84ffb276 | ||
|
|
784c3333d7 | ||
|
|
2227f54e66 | ||
|
|
c55657dca5 | ||
|
|
b6b2c48340 | ||
|
|
582c86a7b6 | ||
|
|
76668e9004 | ||
|
|
d0426a6127 | ||
|
|
4252575e84 | ||
|
|
f6dcd8ac91 | ||
|
|
6a9fb51dc5 | ||
|
|
70b93e2cd4 | ||
|
|
f4493674f7 | ||
|
|
dd5a818d6a | ||
|
|
e78dfb5df6 | ||
|
|
429d0dcda0 | ||
|
|
eb174e1afa | ||
|
|
4a6b5367ca | ||
|
|
3675e616bc | ||
|
|
f9d3df9531 | ||
|
|
081d312a11 | ||
|
|
355e832b02 | ||
|
|
6b65e2b168 | ||
|
|
57041084d1 | ||
|
|
cf4c7b406a | ||
|
|
edb3cdbb59 | ||
|
|
55025d4dc9 | ||
|
|
6f7a5e525d | ||
|
|
342c77c851 | ||
|
|
94f4d81b70 | ||
|
|
74f31c6677 | ||
|
|
7589b9f822 | ||
|
|
b761169976 | ||
|
|
537c3a79ab |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -13,4 +13,5 @@ erpnext/docs/current
|
||||
__pycache__
|
||||
*~
|
||||
.vscode/
|
||||
node_modules/
|
||||
node_modules/
|
||||
.idea/
|
||||
@@ -5,7 +5,7 @@ import frappe
|
||||
from erpnext.hooks import regional_overrides
|
||||
from frappe.utils import getdate
|
||||
|
||||
__version__ = '11.1.52'
|
||||
__version__ = '11.1.60'
|
||||
|
||||
def get_default_company(user=None):
|
||||
'''Get default company for user'''
|
||||
|
||||
@@ -121,7 +121,11 @@ frappe.treeview_settings["Account"] = {
|
||||
},
|
||||
onrender: function(node) {
|
||||
if(frappe.boot.user.can_read.indexOf("GL Entry") !== -1){
|
||||
var dr_or_cr = in_list(["Liability", "Income", "Equity"], node.data.root_type) ? "Cr" : "Dr";
|
||||
|
||||
// show Dr if positive since balance is calculated as debit - credit else show Cr
|
||||
let balance = node.data.balance_in_account_currency || node.data.balance;
|
||||
let dr_or_cr = balance > 0 ? "Dr": "Cr";
|
||||
|
||||
if (node.data && node.data.balance!==undefined) {
|
||||
$('<span class="balance-area pull-right text-muted small">'
|
||||
+ (node.data.balance_in_account_currency ?
|
||||
|
||||
@@ -167,39 +167,7 @@
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Status",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Open\nClosed",
|
||||
"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,
|
||||
@@ -273,7 +241,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-04-13 19:14:47.593753",
|
||||
"modified": "2019-08-01 19:14:47.593753",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Accounting Period",
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe import _
|
||||
|
||||
class OverlapError(frappe.ValidationError): pass
|
||||
|
||||
class AccountingPeriod(Document):
|
||||
def validate(self):
|
||||
@@ -34,12 +36,13 @@ class AccountingPeriod(Document):
|
||||
}, as_dict=True)
|
||||
|
||||
if len(existing_accounting_period) > 0:
|
||||
frappe.throw(_("Accounting Period overlaps with {0}".format(existing_accounting_period[0].get("name"))))
|
||||
frappe.throw(_("Accounting Period overlaps with {0}")
|
||||
.format(existing_accounting_period[0].get("name")), OverlapError)
|
||||
|
||||
def get_doctypes_for_closing(self):
|
||||
docs_for_closing = []
|
||||
#if not self.closed_documents or len(self.closed_documents) == 0:
|
||||
doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", "Bank Reconciliation", "Asset", "Purchase Order", "Sales Order", "Leave Application", "Leave Allocation", "Stock Entry"]
|
||||
doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", "Bank Reconciliation",
|
||||
"Asset", "Purchase Order", "Sales Order", "Leave Application", "Leave Allocation", "Stock Entry"]
|
||||
closed_doctypes = [{"document_type": doctype, "closed": 1} for doctype in doctypes]
|
||||
for closed_doctype in closed_doctypes:
|
||||
docs_for_closing.append(closed_doctype)
|
||||
@@ -52,4 +55,4 @@ class AccountingPeriod(Document):
|
||||
self.append('closed_documents', {
|
||||
"document_type": doctype_for_closing.document_type,
|
||||
"closed": doctype_for_closing.closed
|
||||
})
|
||||
})
|
||||
@@ -5,23 +5,42 @@ from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
from frappe.utils import nowdate, add_months
|
||||
from erpnext.accounts.general_ledger import ClosedAccountingPeriod
|
||||
from erpnext.accounts.doctype.accounting_period.accounting_period import OverlapError
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
|
||||
# class TestAccountingPeriod(unittest.TestCase):
|
||||
# def test_overlap(self):
|
||||
# ap1 = create_accounting_period({"start_date":"2018-04-01", "end_date":"2018-06-30", "company":"Wind Power LLC"})
|
||||
# ap1.save()
|
||||
# ap2 = create_accounting_period({"start_date":"2018-06-30", "end_date":"2018-07-10", "company":"Wind Power LLC"})
|
||||
# self.assertRaises(frappe.OverlapError, accounting_period_2.save())
|
||||
#
|
||||
# def tearDown(self):
|
||||
# pass
|
||||
#
|
||||
#
|
||||
# def create_accounting_period(**args):
|
||||
# accounting_period = frappe.new_doc("Accounting Period")
|
||||
# accounting_period.start_date = args.start_date or frappe.utils.datetime.date(2018, 4, 1)
|
||||
# accounting_period.end_date = args.end_date or frappe.utils.datetime.date(2018, 6, 30)
|
||||
# accounting_period.company = args.company
|
||||
# accounting_period.period_name = "_Test_Period_Name_1"
|
||||
#
|
||||
# return accounting_period
|
||||
class TestAccountingPeriod(unittest.TestCase):
|
||||
def test_overlap(self):
|
||||
ap1 = create_accounting_period(start_date = "2018-04-01",
|
||||
end_date = "2018-06-30", company = "Wind Power LLC")
|
||||
ap1.save()
|
||||
|
||||
ap2 = create_accounting_period(start_date = "2018-06-30",
|
||||
end_date = "2018-07-10", company = "Wind Power LLC", period_name = "Test Accounting Period 1")
|
||||
self.assertRaises(OverlapError, ap2.save)
|
||||
|
||||
def test_accounting_period(self):
|
||||
ap1 = create_accounting_period(period_name = "Test Accounting Period 2")
|
||||
ap1.save()
|
||||
|
||||
doc = create_sales_invoice(do_not_submit=1, cost_center = "_Test Company - _TC", warehouse = "Stores - _TC")
|
||||
self.assertRaises(ClosedAccountingPeriod, doc.submit)
|
||||
|
||||
def tearDown(self):
|
||||
for d in frappe.get_all("Accounting Period"):
|
||||
frappe.delete_doc("Accounting Period", d.name)
|
||||
|
||||
def create_accounting_period(**args):
|
||||
args = frappe._dict(args)
|
||||
|
||||
accounting_period = frappe.new_doc("Accounting Period")
|
||||
accounting_period.start_date = args.start_date or nowdate()
|
||||
accounting_period.end_date = args.end_date or add_months(nowdate(), 1)
|
||||
accounting_period.company = args.company or "_Test Company"
|
||||
accounting_period.period_name =args.period_name or "_Test_Period_Name_1"
|
||||
accounting_period.append("closed_documents", {
|
||||
"document_type": 'Sales Invoice', "closed": 1
|
||||
})
|
||||
|
||||
return accounting_period
|
||||
@@ -50,7 +50,7 @@ class BankTransaction(StatusUpdater):
|
||||
if paid_amount and allocated_amount:
|
||||
if flt(allocated_amount[0]["allocated_amount"]) > flt(paid_amount):
|
||||
frappe.throw(_("The total allocated amount ({0}) is greated than the paid amount ({1}).".format(flt(allocated_amount[0]["allocated_amount"]), flt(paid_amount))))
|
||||
elif flt(allocated_amount[0]["allocated_amount"]) == flt(paid_amount):
|
||||
else:
|
||||
if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]:
|
||||
self.clear_simple_entry(payment_entry)
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
||||
filters: {
|
||||
'account': row.account
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
me.frm.set_query("reference_name", "accounts", function(doc, cdt, cdn) {
|
||||
|
||||
@@ -613,13 +613,18 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre
|
||||
|
||||
orders = []
|
||||
if voucher_type:
|
||||
ref_field = "base_grand_total" if party_account_currency == company_currency else "grand_total"
|
||||
if party_account_currency == company_currency:
|
||||
grand_total_field = "base_grand_total"
|
||||
rounded_total_field = "base_rounded_total"
|
||||
else:
|
||||
grand_total_field = "grand_total"
|
||||
rounded_total_field = "rounded_total"
|
||||
|
||||
orders = frappe.db.sql("""
|
||||
select
|
||||
name as voucher_no,
|
||||
{ref_field} as invoice_amount,
|
||||
({ref_field} - advance_paid) as outstanding_amount,
|
||||
if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount,
|
||||
(if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) - advance_paid) as outstanding_amount,
|
||||
transaction_date as posting_date
|
||||
from
|
||||
`tab{voucher_type}`
|
||||
@@ -627,17 +632,18 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre
|
||||
{party_type} = %s
|
||||
and docstatus = 1
|
||||
and ifnull(status, "") != "Closed"
|
||||
and {ref_field} > advance_paid
|
||||
and if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) > advance_paid
|
||||
and abs(100 - per_billed) > 0.01
|
||||
{condition}
|
||||
order by
|
||||
transaction_date, name
|
||||
""".format(**{
|
||||
"ref_field": ref_field,
|
||||
"rounded_total_field": rounded_total_field,
|
||||
"grand_total_field": grand_total_field,
|
||||
"voucher_type": voucher_type,
|
||||
"party_type": scrub(party_type),
|
||||
"condition": condition
|
||||
}), party, as_dict=True)
|
||||
}), (party), as_dict=True)
|
||||
|
||||
order_list = []
|
||||
for d in orders:
|
||||
@@ -713,9 +719,23 @@ def get_party_details(company, party_type, party, date, cost_center=None):
|
||||
@frappe.whitelist()
|
||||
def get_account_details(account, date, cost_center=None):
|
||||
frappe.has_permission('Payment Entry', throw=True)
|
||||
|
||||
# to check if the passed account is accessible if the reference doctype is Payment Entry
|
||||
account_list = frappe.get_list('Account', {
|
||||
'name': account
|
||||
}, reference_doctype='Payment Entry', limit=1)
|
||||
|
||||
# There might be some user permissions which will allow account under certain doctypes
|
||||
# except for Payment Entry, only in such case we should throw permission error
|
||||
if not account_list:
|
||||
frappe.throw(_('Account: {0} is not permitted under Payment Entry').format(account))
|
||||
|
||||
account_balance = get_balance_on(account, date, cost_center=cost_center,
|
||||
ignore_account_permission=True)
|
||||
|
||||
return frappe._dict({
|
||||
"account_currency": get_account_currency(account),
|
||||
"account_balance": get_balance_on(account, date, cost_center=cost_center),
|
||||
"account_balance": account_balance,
|
||||
"account_type": frappe.db.get_value("Account", account, "account_type")
|
||||
})
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
cur_frm.add_fetch("payment_gateway", "payment_account", "payment_account")
|
||||
cur_frm.add_fetch("payment_gateway", "payment_gateway", "payment_gateway")
|
||||
cur_frm.add_fetch("payment_gateway", "message", "message")
|
||||
cur_frm.add_fetch("payment_gateway", "payment_url_message", "payment_url_message")
|
||||
cur_frm.add_fetch("payment_gateway_account", "payment_account", "payment_account")
|
||||
cur_frm.add_fetch("payment_gateway_account", "payment_gateway", "payment_gateway")
|
||||
cur_frm.add_fetch("payment_gateway_account", "message", "message")
|
||||
|
||||
frappe.ui.form.on("Payment Request", "onload", function(frm, dt, dn){
|
||||
if (frm.doc.reference_doctype) {
|
||||
|
||||
@@ -432,7 +432,6 @@ def get_customer_id(doc, customer=None):
|
||||
|
||||
return cust_id
|
||||
|
||||
|
||||
def make_customer_and_address(customers):
|
||||
customers_list = []
|
||||
for customer, data in iteritems(customers):
|
||||
@@ -449,7 +448,6 @@ def make_customer_and_address(customers):
|
||||
frappe.db.commit()
|
||||
return customers_list
|
||||
|
||||
|
||||
def add_customer(data):
|
||||
customer = data.get('full_name') or data.get('customer')
|
||||
if frappe.db.exists("Customer", customer.strip()):
|
||||
@@ -466,21 +464,18 @@ def add_customer(data):
|
||||
frappe.db.commit()
|
||||
return customer_doc.name
|
||||
|
||||
|
||||
def get_territory(data):
|
||||
if data.get('territory'):
|
||||
return data.get('territory')
|
||||
|
||||
return frappe.db.get_single_value('Selling Settings','territory') or _('All Territories')
|
||||
|
||||
|
||||
def get_customer_group(data):
|
||||
if data.get('customer_group'):
|
||||
return data.get('customer_group')
|
||||
|
||||
return frappe.db.get_single_value('Selling Settings', 'customer_group') or frappe.db.get_value('Customer Group', {'is_group': 0}, 'name')
|
||||
|
||||
|
||||
def make_contact(args, customer):
|
||||
if args.get('email_id') or args.get('phone'):
|
||||
name = frappe.db.get_value('Dynamic Link',
|
||||
@@ -506,7 +501,6 @@ def make_contact(args, customer):
|
||||
doc.flags.ignore_mandatory = True
|
||||
doc.save(ignore_permissions=True)
|
||||
|
||||
|
||||
def make_address(args, customer):
|
||||
if not args.get('address_line1'):
|
||||
return
|
||||
@@ -521,7 +515,10 @@ def make_address(args, customer):
|
||||
address = frappe.get_doc('Address', name)
|
||||
else:
|
||||
address = frappe.new_doc('Address')
|
||||
address.country = frappe.get_cached_value('Company', args.get('company'), 'country')
|
||||
if args.get('company'):
|
||||
address.country = frappe.get_cached_value('Company',
|
||||
args.get('company'), 'country')
|
||||
|
||||
address.append('links', {
|
||||
'link_doctype': 'Customer',
|
||||
'link_name': customer
|
||||
@@ -533,7 +530,6 @@ def make_address(args, customer):
|
||||
address.flags.ignore_mandatory = True
|
||||
address.save(ignore_permissions=True)
|
||||
|
||||
|
||||
def make_email_queue(email_queue):
|
||||
name_list = []
|
||||
for key, data in iteritems(email_queue):
|
||||
@@ -550,7 +546,6 @@ def make_email_queue(email_queue):
|
||||
|
||||
return name_list
|
||||
|
||||
|
||||
def validate_item(doc):
|
||||
for item in doc.get('items'):
|
||||
if not frappe.db.exists('Item', item.get('item_code')):
|
||||
@@ -569,7 +564,6 @@ def validate_item(doc):
|
||||
item_doc.save(ignore_permissions=True)
|
||||
frappe.db.commit()
|
||||
|
||||
|
||||
def submit_invoice(si_doc, name, doc, name_list):
|
||||
try:
|
||||
si_doc.insert()
|
||||
@@ -585,7 +579,6 @@ def submit_invoice(si_doc, name, doc, name_list):
|
||||
|
||||
return name_list
|
||||
|
||||
|
||||
def save_invoice(doc, name, name_list):
|
||||
try:
|
||||
if not frappe.db.exists('Sales Invoice', {'offline_pos_name': name}):
|
||||
|
||||
@@ -78,6 +78,7 @@ class SalesInvoice(SellingController):
|
||||
self.so_dn_required()
|
||||
|
||||
self.validate_proj_cust()
|
||||
self.validate_pos_return()
|
||||
self.validate_with_previous_doc()
|
||||
self.validate_uom_is_integer("stock_uom", "stock_qty")
|
||||
self.validate_uom_is_integer("uom", "qty")
|
||||
@@ -199,6 +200,16 @@ class SalesInvoice(SellingController):
|
||||
if "Healthcare" in active_domains:
|
||||
manage_invoice_submit_cancel(self, "on_submit")
|
||||
|
||||
def validate_pos_return(self):
|
||||
|
||||
if self.is_pos and self.is_return:
|
||||
total_amount_in_payments = 0
|
||||
for payment in self.payments:
|
||||
total_amount_in_payments += payment.amount
|
||||
invoice_total = self.rounded_total or self.grand_total
|
||||
if total_amount_in_payments < invoice_total:
|
||||
frappe.throw(_("Total payments amount can't be greater than {}".format(-invoice_total)))
|
||||
|
||||
def validate_pos_paid_amount(self):
|
||||
if len(self.payments) == 0 and self.is_pos:
|
||||
frappe.throw(_("At least one mode of payment is required for POS invoice."))
|
||||
|
||||
@@ -9,11 +9,13 @@ from frappe.model.meta import get_field_precision
|
||||
from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
|
||||
|
||||
|
||||
class ClosedAccountingPeriod(frappe.ValidationError): pass
|
||||
class StockAccountInvalidTransaction(frappe.ValidationError): pass
|
||||
|
||||
def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, update_outstanding='Yes', from_repost=False):
|
||||
if gl_map:
|
||||
if not cancel:
|
||||
validate_accounting_period(gl_map)
|
||||
gl_map = process_gl_map(gl_map, merge_entries)
|
||||
if gl_map and len(gl_map) > 1:
|
||||
save_entries(gl_map, adv_adj, update_outstanding, from_repost)
|
||||
@@ -22,6 +24,27 @@ def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, upd
|
||||
else:
|
||||
delete_gl_entries(gl_map, adv_adj=adv_adj, update_outstanding=update_outstanding)
|
||||
|
||||
def validate_accounting_period(gl_map):
|
||||
accounting_periods = frappe.db.sql(""" SELECT
|
||||
ap.name as name
|
||||
FROM
|
||||
`tabAccounting Period` ap, `tabClosed Document` cd
|
||||
WHERE
|
||||
ap.name = cd.parent
|
||||
AND ap.company = %(company)s
|
||||
AND cd.closed = 1
|
||||
AND cd.document_type = %(voucher_type)s
|
||||
AND %(date)s between ap.start_date and ap.end_date
|
||||
""", {
|
||||
'date': gl_map[0].posting_date,
|
||||
'company': gl_map[0].company,
|
||||
'voucher_type': gl_map[0].voucher_type
|
||||
}, as_dict=1)
|
||||
|
||||
if accounting_periods:
|
||||
frappe.throw(_("You can't create accounting entries in the closed accounting period {0}")
|
||||
.format(accounting_periods[0].name), ClosedAccountingPeriod)
|
||||
|
||||
def process_gl_map(gl_map, merge_entries=True):
|
||||
if merge_entries:
|
||||
gl_map = merge_similar_entries(gl_map)
|
||||
|
||||
@@ -1762,6 +1762,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
this.si_docs = this.get_submitted_invoice() || [];
|
||||
this.email_queue_list = this.get_email_queue() || {};
|
||||
this.customers_list = this.get_customers_details() || {};
|
||||
|
||||
if(this.customer_doc) {
|
||||
this.freeze = this.customer_doc.display
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ def _get_party_details(party=None, account=None, party_type="Customer", company=
|
||||
set_other_values(out, party, party_type)
|
||||
set_price_list(out, party, party_type, price_list, pos_profile)
|
||||
|
||||
out["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company, out.customer_group, out.supplier_type)
|
||||
out["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company, out.customer_group, out.supplier_group)
|
||||
|
||||
if fetch_payment_terms_template:
|
||||
out["payment_terms_template"] = get_pyt_term_template(party.name, party_type, company)
|
||||
@@ -450,7 +450,9 @@ 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}")
|
||||
or (timeline_doctype in ("{doctype}") and timeline_name="{name}")
|
||||
or (reference_doctype in ("Quotation", "Opportunity") and timeline_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,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
{%- macro render_currency(df, doc) -%}
|
||||
<div class="row {% if df.bold %}important{% endif %} data-field">
|
||||
<div class="col-xs-{{ "9" if df.fieldtype=="Check" else "5" }}
|
||||
{%- if doc._align_labels_right %} text-right{%- endif -%}">
|
||||
{%- if doc.align_labels_right %} text-right{%- endif -%}">
|
||||
<label>{{ _(df.label) }}</label>
|
||||
</div>
|
||||
<div class="col-xs-{{ "3" if df.fieldtype=="Check" else "7" }} value">
|
||||
@@ -23,7 +23,7 @@
|
||||
{%- for charge in data -%}
|
||||
{%- if (charge.tax_amount or doc.flags.print_taxes_with_zero_amount) and (not charge.included_in_print_rate or doc.flags.show_inclusive_tax_in_print) -%}
|
||||
<div class="row">
|
||||
<div class="col-xs-5 {%- if doc._align_labels_right %} text-right{%- endif -%}">
|
||||
<div class="col-xs-5 {%- if doc.align_labels_right %} text-right{%- endif -%}">
|
||||
<label>{{ charge.get_formatted("description") }}</label></div>
|
||||
<div class="col-xs-7 text-right">
|
||||
{{ frappe.utils.fmt_money((charge.tax_amount)|int|abs, currency=doc.currency) }}
|
||||
@@ -103,8 +103,8 @@
|
||||
{% for section in page %}
|
||||
<div class="row section-break">
|
||||
{% if section.columns.fields %}
|
||||
{%- if doc._line_breaks and loop.index != 1 -%}<hr>{%- endif -%}
|
||||
{%- if doc._show_section_headings and section.label and section.has_data -%}
|
||||
{%- if doc.print_line_breaks and loop.index != 1 -%}<hr>{%- endif -%}
|
||||
{%- if doc.print_section_headings and section.label and section.has_data -%}
|
||||
<h4 class='col-sm-12'>{{ _(section.label) }}</h4>
|
||||
{% endif %}
|
||||
{%- endif -%}
|
||||
|
||||
@@ -1,275 +1,269 @@
|
||||
<style>
|
||||
.print-format {
|
||||
padding: 4mm;
|
||||
font-size: 8.0pt !important;
|
||||
}
|
||||
.print-format td {
|
||||
vertical-align:middle !important;
|
||||
}
|
||||
</style>
|
||||
.print-format {
|
||||
padding: 4mm;
|
||||
font-size: 8.0pt !important;
|
||||
}
|
||||
.print-format td {
|
||||
vertical-align:middle !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<h2 class="text-center" style="margin-top:0">{%= __(report.report_name) %}</h2>
|
||||
<h4 class="text-center">
|
||||
{% if (filters.customer_name) { %}
|
||||
{%= filters.customer_name %}
|
||||
{% } else { %}
|
||||
{%= filters.customer || filters.supplier %}
|
||||
{% } %}
|
||||
</h4>
|
||||
<h6 class="text-center">
|
||||
{% if (filters.tax_id) { %}
|
||||
{%= __("Tax Id: ")%} {%= filters.tax_id %}
|
||||
<h2 class="text-center" style="margin-top:0">{%= __(report.report_name) %}</h2>
|
||||
<h4 class="text-center">
|
||||
{% if (filters.customer_name) { %}
|
||||
{%= filters.customer_name %}
|
||||
{% } else { %}
|
||||
{%= filters.customer || filters.supplier %}
|
||||
{% } %}
|
||||
</h6>
|
||||
<h5 class="text-center">
|
||||
{%= __(filters.ageing_based_on) %}
|
||||
{%= __("Until") %}
|
||||
{%= frappe.datetime.str_to_user(filters.report_date) %}
|
||||
</h5>
|
||||
</h4>
|
||||
<h6 class="text-center">
|
||||
{% if (filters.tax_id) { %}
|
||||
{%= __("Tax Id: ")%} {%= filters.tax_id %}
|
||||
{% } %}
|
||||
</h6>
|
||||
<h5 class="text-center">
|
||||
{%= __(filters.ageing_based_on) %}
|
||||
{%= __("Until") %}
|
||||
{%= frappe.datetime.str_to_user(filters.report_date) %}
|
||||
</h5>
|
||||
|
||||
<div class="clearfix">
|
||||
<div class="pull-left">
|
||||
{% if(filters.payment_terms) { %}
|
||||
<strong>{%= __("Payment Terms") %}:</strong> {%= filters.payment_terms %}
|
||||
{% } %}
|
||||
<div class="clearfix">
|
||||
<div class="pull-left">
|
||||
{% if(filters.payment_terms) { %}
|
||||
<strong>{%= __("Payment Terms") %}:</strong> {%= filters.payment_terms %}
|
||||
{% } %}
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
{% if(filters.credit_limit) { %}
|
||||
<strong>{%= __("Credit Limit") %}:</strong> {%= format_currency(filters.credit_limit) %}
|
||||
{% } %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
{% if(filters.credit_limit) { %}
|
||||
<strong>{%= __("Credit Limit") %}:</strong> {%= format_currency(filters.credit_limit) %}
|
||||
|
||||
{% if(filters.show_future_payments) { %}
|
||||
{% var balance_row = data.slice(-1).pop();
|
||||
var range1 = report.columns[11].label;
|
||||
var range2 = report.columns[12].label;
|
||||
var range3 = report.columns[13].label;
|
||||
var range4 = report.columns[14].label;
|
||||
var range5 = report.columns[15].label;
|
||||
%}
|
||||
{% if(balance_row) { %}
|
||||
<table class="table table-bordered table-condensed">
|
||||
<caption class="text-right">(Amount in {%= data[0]["currency"] || "" %})</caption>
|
||||
<colgroup>
|
||||
<col style="width: 30mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
</colgroup>
|
||||
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{%= __(" ") %}</th>
|
||||
<th>{%= __(range1) %}</th>
|
||||
<th>{%= __(range2) %}</th>
|
||||
<th>{%= __(range3) %}</th>
|
||||
<th>{%= __(range4) %}</th>
|
||||
<th>{%= __(range5) %}</th>
|
||||
<th>{%= __("Total") %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{%= __("Total Outstanding") %}</td>
|
||||
<td class="text-right">{%= format_number(balance_row["range1"], null, 2) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row["range2"]) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row["range3"]) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row["range4"]) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row["range5"]) %}</td>
|
||||
<td class="text-right">
|
||||
{%= format_currency(flt(balance_row["outstanding"]), data[data.length-1]["currency"]) %}
|
||||
</td>
|
||||
</tr>
|
||||
<td>{%= __("Future Payments") %}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td class="text-right">
|
||||
{%= format_currency(flt(balance_row[("future_amount")]), data[data.length-1]["currency"]) %}
|
||||
</td>
|
||||
<tr class="cvs-footer">
|
||||
<th class="text-left">{%= __("Cheques Required") %}</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th class="text-right">
|
||||
{%= format_currency(flt(balance_row["outstanding"] - balance_row[("future_amount")]), data[data.length-1]["currency"]) %}</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
{% } %}
|
||||
{% } %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if(filters.show_pdc_in_print) { %}
|
||||
{% var balance_row = data.slice(-1).pop();
|
||||
var range1 = report.columns[11].label;
|
||||
var range2 = report.columns[12].label;
|
||||
var range3 = report.columns[13].label;
|
||||
var range4 = report.columns[14].label;
|
||||
var range5 = report.columns[15].label;
|
||||
var range6 = report.columns[16].label;
|
||||
%}
|
||||
{% if(balance_row) { %}
|
||||
<table class="table table-bordered table-condensed">
|
||||
<caption class="text-right">(Amount in {%= data[0][__("currency")] || "" %})</caption>
|
||||
<colgroup>
|
||||
<col style="width: 30mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
<col style="width: 18mm;">
|
||||
</colgroup>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{%= __(" ") %}</th>
|
||||
<th>{%= __(range1) %}</th>
|
||||
<th>{%= __(range2) %}</th>
|
||||
<th>{%= __(range3) %}</th>
|
||||
<th>{%= __(range4) %}</th>
|
||||
<th>{%= __(range5) %}</th>
|
||||
<th>{%= __(range6) %}</th>
|
||||
<th>{%= __("Total") %}</th>
|
||||
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
|
||||
<th style="width: 10%">{%= __("Date") %}</th>
|
||||
<th style="width: 4%">{%= __("Age (Days)") %}</th>
|
||||
|
||||
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %}
|
||||
<th style="width: 14%">{%= __("Reference") %}</th>
|
||||
<th style="width: 10%">{%= __("Sales Person") %}</th>
|
||||
{% } else { %}
|
||||
<th style="width: 24%">{%= __("Reference") %}</th>
|
||||
{% } %}
|
||||
{% if(!filters.show_future_payments) { %}
|
||||
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
|
||||
{% } %}
|
||||
<th style="width: 10%; text-align: right">{%= __("Invoiced Amount") %}</th>
|
||||
{% if(!filters.show_future_payments) { %}
|
||||
<th style="width: 10%; text-align: right">{%= __("Paid Amount") %}</th>
|
||||
<th style="width: 10%; text-align: right">{%= report.report_name === "Accounts Receivable" ? __('Credit Note') : __('Debit Note') %}</th>
|
||||
{% } %}
|
||||
<th style="width: 10%; text-align: right">{%= __("Outstanding Amount") %}</th>
|
||||
{% if(filters.show_future_payments) { %}
|
||||
{% if(report.report_name === "Accounts Receivable") { %}
|
||||
<th style="width: 12%">{%= __("Customer LPO No.") %}</th>
|
||||
{% } %}
|
||||
<th style="width: 10%">{%= __("Future Payment Ref") %}</th>
|
||||
<th style="width: 10%">{%= __("Future Payment Amount") %}</th>
|
||||
<th style="width: 10%">{%= __("Remaining Balance") %}</th>
|
||||
{% } %}
|
||||
{% } else { %}
|
||||
<th style="width: 40%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
|
||||
<th style="width: 15%">{%= __("Total Invoiced Amount") %}</th>
|
||||
<th style="width: 15%">{%= __("Total Paid Amount") %}</th>
|
||||
<th style="width: 15%">{%= report.report_name === "Accounts Receivable Summary" ? __('Credit Note Amount') : __('Debit Note Amount') %}</th>
|
||||
<th style="width: 15%">{%= __("Total Outstanding Amount") %}</th>
|
||||
{% } %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{%= __("Total Outstanding") %}</td>
|
||||
<td class="text-right">{%= format_number(balance_row[range1], null, 2) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row[range2]) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row[range3]) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row[range4]) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row[range5]) %}</td>
|
||||
<td class="text-right">{%= format_currency(balance_row[range6]) %}</td>
|
||||
<td class="text-right">
|
||||
{%= format_currency(flt(balance_row[("outstanding_amount")]), data[data.length-1]["currency"]) %}
|
||||
</td>
|
||||
</tr>
|
||||
<td>{%= __("PDC/LC") %}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td class="text-right">
|
||||
{%= format_currency(flt(balance_row[("pdc/lc_amount")]), data[data.length-1]["currency"]) %}
|
||||
</td>
|
||||
<tr class="cvs-footer">
|
||||
<th class="text-left">{%= __("Cheques Required") %}</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th class="text-right">
|
||||
{%= format_currency(flt(balance_row[("outstanding_amount")]-balance_row[("pdc/lc_amount")]), data[data.length-1]["currency"]) %}</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
{% } %}
|
||||
{% } %}
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
|
||||
<th style="width: 10%">{%= __("Date") %}</th>
|
||||
<th style="width: 4%">{%= __("Age (Days)") %}</th>
|
||||
|
||||
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
|
||||
<th style="width: 14%">{%= __("Reference") %}</th>
|
||||
<th style="width: 10%">{%= __("Sales Person") %}</th>
|
||||
{% } else { %}
|
||||
<th style="width: 24%">{%= __("Reference") %}</th>
|
||||
{% } %}
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
|
||||
{% } %}
|
||||
<th style="width: 10%; text-align: right">{%= __("Invoiced Amount") %}</th>
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
<th style="width: 10%; text-align: right">{%= __("Paid Amount") %}</th>
|
||||
<th style="width: 10%; text-align: right">{%= report.report_name === "Accounts Receivable" ? __('Credit Note') : __('Debit Note') %}</th>
|
||||
{% } %}
|
||||
<th style="width: 10%; text-align: right">{%= __("Outstanding Amount") %}</th>
|
||||
{% if(filters.show_pdc_in_print) { %}
|
||||
{% if(report.report_name === "Accounts Receivable") { %}
|
||||
<th style="width: 12%">{%= __("Customer LPO No.") %}</th>
|
||||
{% } %}
|
||||
<th style="width: 10%">{%= __("PDC/LC Ref") %}</th>
|
||||
<th style="width: 10%">{%= __("PDC/LC Amount") %}</th>
|
||||
<th style="width: 10%">{%= __("Remaining Balance") %}</th>
|
||||
{% } %}
|
||||
{% } else { %}
|
||||
<th style="width: 40%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
|
||||
<th style="width: 15%">{%= __("Total Invoiced Amount") %}</th>
|
||||
<th style="width: 15%">{%= __("Total Paid Amount") %}</th>
|
||||
<th style="width: 15%">{%= report.report_name === "Accounts Receivable Summary" ? __('Credit Note Amount') : __('Debit Note Amount') %}</th>
|
||||
<th style="width: 15%">{%= __("Total Outstanding Amount") %}</th>
|
||||
{% } %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for(var i=0, l=data.length; i<l; i++) { %}
|
||||
<tr>
|
||||
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
|
||||
{% if(data[i][__("Customer")] || data[i][__("Supplier")]) { %}
|
||||
<td>{%= frappe.datetime.str_to_user(data[i]["posting_date"]) %}</td>
|
||||
<td style="text-align: right">{%= data[i][__("Age (Days)")] %}</td>
|
||||
<td>
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
{%= data[i]["voucher_type"] %}
|
||||
<br>
|
||||
{% } %}
|
||||
{%= data[i]["voucher_no"] %}
|
||||
</td>
|
||||
|
||||
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
|
||||
<td>{%= data[i]["sales_person"] %}</td>
|
||||
{% } %}
|
||||
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
<td>
|
||||
{% if(!(filters.customer || filters.supplier)) { %}
|
||||
{%= data[i][__("Customer")] || data[i][__("Supplier")] %}
|
||||
{% if(data[i][__("Customer Name")] && data[i][__("Customer Name")] != data[i][__("Customer")]) { %}
|
||||
<br> {%= data[i][__("Customer Name")] %}
|
||||
{% } else if(data[i][__("Supplier Name")] != data[i][__("Supplier")]) { %}
|
||||
<br> {%= data[i][__("Supplier Name")] %}
|
||||
{% for(var i=0, l=data.length; i<l; i++) { %}
|
||||
<tr>
|
||||
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
|
||||
{% if(data[i]["party"]) { %}
|
||||
<td>{%= frappe.datetime.str_to_user(data[i]["posting_date"]) %}</td>
|
||||
<td style="text-align: right">{%= data[i]["age"] %}</td>
|
||||
<td>
|
||||
{% if(!filters.show_future_payments) { %}
|
||||
{%= data[i]["voucher_type"] %}
|
||||
<br>
|
||||
{% } %}
|
||||
{%= data[i]["voucher_no"] %}
|
||||
</td>
|
||||
|
||||
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %}
|
||||
<td>{%= data[i]["sales_person"] %}</td>
|
||||
{% } %}
|
||||
<div>
|
||||
{% if data[i][__("Remarks")] %}
|
||||
{%= __("Remarks") %}:
|
||||
{%= data[i][__("Remarks")] %}
|
||||
{% } %}
|
||||
</div>
|
||||
</td>
|
||||
{% } %}
|
||||
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["invoiced_amount"], data[i]["currency"]) %}</td>
|
||||
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["paid_amount"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">
|
||||
{%= report.report_name === "Accounts Receivable" ? format_currency(data[i]["credit_note"], data[i]["currency"]) : format_currency(data[i]["debit_note"], data[i]["currency"]) %}</td>
|
||||
{% } %}
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["outstanding_amount"], data[i]["currency"]) %}</td>
|
||||
|
||||
{% if(filters.show_pdc_in_print) { %}
|
||||
{% if(report.report_name === "Accounts Receivable") { %}
|
||||
<td style="text-align: right">
|
||||
{%= data[i]["po_no"] %}</td>
|
||||
{% } %}
|
||||
<td style="text-align: right">{%= data[i][("pdc/lc_ref")] %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i][("pdc/lc_amount")], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i][("remaining_balance")], data[i]["currency"]) %}</td>
|
||||
{% } %}
|
||||
{% } else { %}
|
||||
<td></td>
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
<td></td>
|
||||
{% } %}
|
||||
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
|
||||
<td></td>
|
||||
{% } %}
|
||||
<td></td>
|
||||
<td style="text-align: right"><b>{%= __("Total") %}</b></td>
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["invoiced_amount"], data[i]["currency"] ) %}</td>
|
||||
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["paid_amount"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= report.report_name === "Accounts Receivable" ? format_currency(data[i]["credit_note"], data[i]["currency"]) : format_currency(data[i]["debit_note"], data[i]["currency"]) %} </td>
|
||||
{% } %}
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["outstanding_amount"], data[i]["currency"]) %}</td>
|
||||
|
||||
{% if(filters.show_pdc_in_print) { %}
|
||||
{% if(report.report_name === "Accounts Receivable") { %}
|
||||
<td style="text-align: right">
|
||||
{%= data[i][__("Customer LPO")] %}</td>
|
||||
{% } %}
|
||||
<td style="text-align: right">{%= data[i][("pdc/lc_ref")] %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i][("pdc/lc_amount")], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i][("remaining_balance")], data[i]["currency"]) %}</td>
|
||||
{% } %}
|
||||
{% } %}
|
||||
{% } else { %}
|
||||
{% if(data[i][__("Customer")] || data[i][__("Supplier")]|| " ") { %}
|
||||
{% if((data[i][__("Customer")] || data[i][__("Supplier")]) != __("'Total'")) { %}
|
||||
{% if(!filters.show_future_payments) { %}
|
||||
<td>
|
||||
{% if(!(filters.customer || filters.supplier)) { %}
|
||||
{%= data[i][__("Customer")] || data[i][__("Supplier")] %}
|
||||
{% if(data[i][__("Customer Name")] && data[i][__("Customer Name")] != data[i][__("Customer")]) { %}
|
||||
<br> {%= data[i][__("Customer Name")] %}
|
||||
{% } else if(data[i][__("Supplier Name")] != data[i][__("Supplier")]) { %}
|
||||
<br> {%= data[i][__("Supplier Name")] %}
|
||||
{%= data[i]["party"] %}
|
||||
{% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %}
|
||||
<br> {%= data[i]["customer_name"] %}
|
||||
{% } else if(data[i]["supplier_name"] != data[i]["party"]) { %}
|
||||
<br> {%= data[i]["supplier_name"] %}
|
||||
{% } %}
|
||||
{% } %}
|
||||
<br>{%= __("Remarks") %}:
|
||||
{%= data[i][__("Remarks")] %}
|
||||
<div>
|
||||
{% if data[i]["remarks"] %}
|
||||
{%= __("Remarks") %}:
|
||||
{%= data[i]["remarks"] %}
|
||||
{% } %}
|
||||
</div>
|
||||
</td>
|
||||
{% } %}
|
||||
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["invoiced"], data[i]["currency"]) %}</td>
|
||||
|
||||
{% if(!filters.show_future_payments) { %}
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["paid"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %}</td>
|
||||
{% } %}
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}</td>
|
||||
|
||||
{% if(filters.show_future_payments) { %}
|
||||
{% if(report.report_name === "Accounts Receivable") { %}
|
||||
<td style="text-align: right">
|
||||
{%= data[i]["po_no"] %}</td>
|
||||
{% } %}
|
||||
<td style="text-align: right">{%= data[i]["future_ref"] %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["future_amount"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["remaining_balance"], data[i]["currency"]) %}</td>
|
||||
{% } %}
|
||||
{% } else { %}
|
||||
<td><b>{%= __("Total") %}</b></td>
|
||||
<td></td>
|
||||
{% if(!filters.show_future_payments) { %}
|
||||
<td></td>
|
||||
{% } %}
|
||||
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %}
|
||||
<td></td>
|
||||
{% } %}
|
||||
<td></td>
|
||||
<td style="text-align: right"><b>{%= __("Total") %}</b></td>
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["invoiced"], data[i]["currency"] ) %}</td>
|
||||
|
||||
{% if(!filters.show_future_payments) { %}
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["paid"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %} </td>
|
||||
{% } %}
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}</td>
|
||||
|
||||
{% if(filters.show_future_payments) { %}
|
||||
{% if(report.report_name === "Accounts Receivable") { %}
|
||||
<td style="text-align: right">
|
||||
{%= data[i]["po_no"] %}</td>
|
||||
{% } %}
|
||||
<td style="text-align: right">{%= data[i]["future_ref"] %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["future_amount"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["remaining_balance"], data[i]["currency"]) %}</td>
|
||||
{% } %}
|
||||
{% } %}
|
||||
{% } else { %}
|
||||
{% if(data[i]["party"]|| " ") { %}
|
||||
{% if((data[i]["party"]) != __("'Total'")) { %}
|
||||
<td>
|
||||
{% if(!(filters.customer || filters.supplier)) { %}
|
||||
{%= data[i]["party"] %}
|
||||
{% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %}
|
||||
<br> {%= data[i]["customer_name"] %}
|
||||
{% } else if(data[i]["supplier_name"] != data[i]["party"]) { %}
|
||||
<br> {%= data[i]["supplier_name"] %}
|
||||
{% } %}
|
||||
{% } %}
|
||||
<br>{%= __("Remarks") %}:
|
||||
{%= data[i]["remarks"] %}
|
||||
</td>
|
||||
{% } else { %}
|
||||
<td><b>{%= __("Total") %}</b></td>
|
||||
{% } %}
|
||||
<td style="text-align: right">{%= format_currency(data[i]["invoiced"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["paid"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}</td>
|
||||
{% } %}
|
||||
<td style="text-align: right">{%= format_currency(data[i][("total_invoiced_amt")], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i][("total_paid_amt")], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= report.report_name === "Accounts Receivable Summary" ? format_currency(data[i][__("credit_note_amt")], data[i]["currency"]) : format_currency(data[i][__("debit_note_amt")], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= format_currency(data[i][("total_outstanding_amt")], data[i]["currency"]) %}</td>
|
||||
{% } %}
|
||||
</tr>
|
||||
{% } %}
|
||||
</tr>
|
||||
{% } %}
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="text-right text-muted">{{ __("Printed On ") }}{%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}</p>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="text-right text-muted">{{ __("Printed On ") }}{%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}</p>
|
||||
|
||||
@@ -115,13 +115,18 @@ frappe.query_reports["Accounts Receivable"] = {
|
||||
"fieldtype": "Check",
|
||||
},
|
||||
{
|
||||
"fieldname":"show_pdc_in_print",
|
||||
"label": __("Show PDC in Print"),
|
||||
"fieldname":"show_future_payments",
|
||||
"label": __("Show Future Payments"),
|
||||
"fieldtype": "Check",
|
||||
},
|
||||
{
|
||||
"fieldname":"show_sales_person_in_print",
|
||||
"label": __("Show Sales Person in Print"),
|
||||
"fieldname":"show_delivery_notes",
|
||||
"label": __("Show Delivery Notes"),
|
||||
"fieldtype": "Check",
|
||||
},
|
||||
{
|
||||
"fieldname":"show_sales_person",
|
||||
"label": __("Show Sales Person"),
|
||||
"fieldtype": "Check",
|
||||
},
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,33 +14,44 @@ class TestAccountsReceivable(unittest.TestCase):
|
||||
|
||||
filters = {
|
||||
'company': '_Test Company 2',
|
||||
'based_on_payment_terms': 1
|
||||
'based_on_payment_terms': 1,
|
||||
'report_date': today(),
|
||||
'range1': 30,
|
||||
'range2': 60,
|
||||
'range3': 90,
|
||||
'range4': 120
|
||||
}
|
||||
|
||||
# check invoice grand total and invoiced column's value for 3 payment terms
|
||||
name = make_sales_invoice()
|
||||
report = execute(filters)
|
||||
|
||||
expected_data = [[100,30], [100,50], [100,20]]
|
||||
expected_data = [[100, 30], [100, 50], [100, 20]]
|
||||
|
||||
self.assertEqual(expected_data[0], report[1][0][7:9])
|
||||
self.assertEqual(expected_data[1], report[1][1][7:9])
|
||||
self.assertEqual(expected_data[2], report[1][2][7:9])
|
||||
for i in range(3):
|
||||
row = report[1][i-1]
|
||||
self.assertEqual(expected_data[i-1], [row.invoice_grand_total, row.invoiced])
|
||||
|
||||
# check invoice grand total, invoiced, paid and outstanding column's value after payment
|
||||
make_payment(name)
|
||||
report = execute(filters)
|
||||
|
||||
expected_data_after_payment = [[100,50], [100,20]]
|
||||
expected_data_after_payment = [[100, 50, 10, 40], [100, 20, 0, 20]]
|
||||
|
||||
self.assertEqual(expected_data_after_payment[0], report[1][0][7:9])
|
||||
self.assertEqual(expected_data_after_payment[1], report[1][1][7:9])
|
||||
for i in range(2):
|
||||
row = report[1][i-1]
|
||||
self.assertEqual(expected_data_after_payment[i-1],
|
||||
[row.invoice_grand_total, row.invoiced, row.paid, row.outstanding])
|
||||
|
||||
# check invoice grand total, invoiced, paid and outstanding column's value after credit note
|
||||
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][7:12])
|
||||
expected_data_after_credit_note = [100, 0, 0, 40, -40]
|
||||
|
||||
row = report[1][0]
|
||||
self.assertEqual(expected_data_after_credit_note,
|
||||
[row.invoice_grand_total, row.invoiced, row.paid, row.credit_note, row.outstanding])
|
||||
|
||||
def make_sales_invoice():
|
||||
frappe.set_user("Administrator")
|
||||
@@ -64,7 +75,7 @@ def make_sales_invoice():
|
||||
return si.name
|
||||
|
||||
def make_payment(docname):
|
||||
pe = get_payment_entry("Sales Invoice", docname, bank_account="Cash - _TC2", party_amount=30)
|
||||
pe = get_payment_entry("Sales Invoice", docname, bank_account="Cash - _TC2", party_amount=40)
|
||||
pe.paid_from = "Debtors - _TC2"
|
||||
pe.insert()
|
||||
pe.submit()
|
||||
|
||||
@@ -3,236 +3,11 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _, scrub
|
||||
from frappe.utils import flt
|
||||
from frappe import _
|
||||
from frappe.utils import flt, cint
|
||||
from erpnext.accounts.party import get_partywise_advanced_payment_amount
|
||||
from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport
|
||||
|
||||
from six import iteritems
|
||||
from six.moves import zip
|
||||
|
||||
class AccountsReceivableSummary(ReceivablePayableReport):
|
||||
def run(self, args):
|
||||
party_naming_by = frappe.db.get_value(args.get("naming_by")[0], None, args.get("naming_by")[1])
|
||||
return self.get_columns(party_naming_by, args), self.get_data(party_naming_by, args)
|
||||
|
||||
def get_columns(self, party_naming_by, args):
|
||||
columns = [_(args.get("party_type")) + ":Link/" + args.get("party_type") + ":200"]
|
||||
|
||||
if party_naming_by == "Naming Series":
|
||||
columns += [ args.get("party_type") + " Name::140"]
|
||||
|
||||
credit_debit_label = "Credit Note Amt" if args.get('party_type') == 'Customer' else "Debit Note Amt"
|
||||
|
||||
columns += [{
|
||||
"label": _("Advance Amount"),
|
||||
"fieldname": "advance_amount",
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 100
|
||||
},{
|
||||
"label": _("Total Invoiced Amt"),
|
||||
"fieldname": "total_invoiced_amt",
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"label": _("Total Paid Amt"),
|
||||
"fieldname": "total_paid_amt",
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 100
|
||||
}]
|
||||
|
||||
columns += [
|
||||
{
|
||||
"label": _(credit_debit_label),
|
||||
"fieldname": scrub(credit_debit_label),
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 140
|
||||
},
|
||||
{
|
||||
"label": _("Total Outstanding Amt"),
|
||||
"fieldname": "total_outstanding_amt",
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 160
|
||||
},
|
||||
{
|
||||
"label": _("0-" + str(self.filters.range1)),
|
||||
"fieldname": scrub("0-" + str(self.filters.range1)),
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 160
|
||||
},
|
||||
{
|
||||
"label": _(str(self.filters.range1) + "-" + str(self.filters.range2)),
|
||||
"fieldname": scrub(str(self.filters.range1) + "-" + str(self.filters.range2)),
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 160
|
||||
},
|
||||
{
|
||||
"label": _(str(self.filters.range2) + "-" + str(self.filters.range3)),
|
||||
"fieldname": scrub(str(self.filters.range2) + "-" + str(self.filters.range3)),
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 160
|
||||
},
|
||||
{
|
||||
"label": _(str(self.filters.range3) + "-" + str(self.filters.range4)),
|
||||
"fieldname": scrub(str(self.filters.range3) + "-" + str(self.filters.range4)),
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 160
|
||||
},
|
||||
{
|
||||
"label": _(str(self.filters.range4) + _("-Above")),
|
||||
"fieldname": scrub(str(self.filters.range4) + _("-Above")),
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 160
|
||||
}
|
||||
]
|
||||
|
||||
if args.get("party_type") == "Customer":
|
||||
columns += [{
|
||||
"label": _("Territory"),
|
||||
"fieldname": "territory",
|
||||
"fieldtype": "Link",
|
||||
"options": "Territory",
|
||||
"width": 80
|
||||
},
|
||||
{
|
||||
"label": _("Customer Group"),
|
||||
"fieldname": "customer_group",
|
||||
"fieldtype": "Link",
|
||||
"options": "Customer Group",
|
||||
"width": 80
|
||||
},
|
||||
{
|
||||
"label": _("Sales Person"),
|
||||
"fieldtype": "Data",
|
||||
"fieldname": "sales_person",
|
||||
"width": 120,
|
||||
}]
|
||||
|
||||
if args.get("party_type") == "Supplier":
|
||||
columns += [{
|
||||
"label": _("Supplier Group"),
|
||||
"fieldname": "supplier_group",
|
||||
"fieldtype": "Link",
|
||||
"options": "Supplier Group",
|
||||
"width": 80
|
||||
}]
|
||||
|
||||
columns.append({
|
||||
"fieldname": "currency",
|
||||
"label": _("Currency"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Currency",
|
||||
"width": 80
|
||||
})
|
||||
|
||||
return columns
|
||||
|
||||
def get_data(self, party_naming_by, args):
|
||||
data = []
|
||||
|
||||
partywise_total = self.get_partywise_total(party_naming_by, args)
|
||||
|
||||
partywise_advance_amount = get_partywise_advanced_payment_amount(args.get("party_type"),
|
||||
self.filters.get("report_date")) or {}
|
||||
for party, party_dict in iteritems(partywise_total):
|
||||
row = [party]
|
||||
|
||||
if party_naming_by == "Naming Series":
|
||||
row += [self.get_party_name(args.get("party_type"), party)]
|
||||
|
||||
row += [partywise_advance_amount.get(party, 0)]
|
||||
|
||||
paid_amt = 0
|
||||
if party_dict.paid_amt > 0:
|
||||
paid_amt = flt(party_dict.paid_amt - partywise_advance_amount.get(party, 0))
|
||||
|
||||
row += [
|
||||
party_dict.invoiced_amt, paid_amt, party_dict.credit_amt, party_dict.outstanding_amt,
|
||||
party_dict.range1, party_dict.range2, party_dict.range3, party_dict.range4, party_dict.range5
|
||||
]
|
||||
|
||||
if args.get("party_type") == "Customer":
|
||||
row += [self.get_territory(party), self.get_customer_group(party), ", ".join(set(party_dict.sales_person))]
|
||||
if args.get("party_type") == "Supplier":
|
||||
row += [self.get_supplier_group(party)]
|
||||
|
||||
row.append(party_dict.currency)
|
||||
data.append(row)
|
||||
|
||||
return data
|
||||
|
||||
def get_partywise_total(self, party_naming_by, args):
|
||||
party_total = frappe._dict()
|
||||
for d in self.get_voucherwise_data(party_naming_by, args):
|
||||
party_total.setdefault(d.party,
|
||||
frappe._dict({
|
||||
"invoiced_amt": 0,
|
||||
"paid_amt": 0,
|
||||
"credit_amt": 0,
|
||||
"outstanding_amt": 0,
|
||||
"range1": 0,
|
||||
"range2": 0,
|
||||
"range3": 0,
|
||||
"range4": 0,
|
||||
"range5": 0,
|
||||
"sales_person": []
|
||||
})
|
||||
)
|
||||
for k in list(party_total[d.party]):
|
||||
if k not in ["currency", "sales_person"]:
|
||||
party_total[d.party][k] += flt(d.get(k, 0))
|
||||
|
||||
party_total[d.party].currency = d.currency
|
||||
|
||||
if d.sales_person:
|
||||
party_total[d.party].sales_person.append(d.sales_person)
|
||||
|
||||
return party_total
|
||||
|
||||
def get_voucherwise_data(self, party_naming_by, args):
|
||||
voucherwise_data = ReceivablePayableReport(self.filters).run(args)[1]
|
||||
|
||||
cols = ["posting_date", "party"]
|
||||
|
||||
if party_naming_by == "Naming Series":
|
||||
cols += ["party_name"]
|
||||
|
||||
if args.get("party_type") == 'Customer':
|
||||
cols += ["contact"]
|
||||
|
||||
cols += ["voucher_type", "voucher_no", "due_date"]
|
||||
|
||||
if args.get("party_type") == "Supplier":
|
||||
cols += ["bill_no", "bill_date"]
|
||||
|
||||
cols += ["invoiced_amt", "paid_amt", "credit_amt",
|
||||
"outstanding_amt", "age", "range1", "range2", "range3", "range4", "range5", "currency", "pdc/lc_date", "pdc/lc_ref",
|
||||
"pdc/lc_amount"]
|
||||
|
||||
if args.get("party_type") == "Supplier":
|
||||
cols += ["supplier_group", "remarks"]
|
||||
if args.get("party_type") == "Customer":
|
||||
cols += ["po_no", "do_no", "territory", "customer_group", "sales_person", "remarks"]
|
||||
|
||||
return self.make_data_dict(cols, voucherwise_data)
|
||||
|
||||
def make_data_dict(self, cols, data):
|
||||
data_dict = []
|
||||
for d in data:
|
||||
data_dict.append(frappe._dict(zip(cols, d)))
|
||||
|
||||
return data_dict
|
||||
|
||||
def execute(filters=None):
|
||||
args = {
|
||||
@@ -241,3 +16,119 @@ def execute(filters=None):
|
||||
}
|
||||
|
||||
return AccountsReceivableSummary(filters).run(args)
|
||||
|
||||
class AccountsReceivableSummary(ReceivablePayableReport):
|
||||
def run(self, args):
|
||||
self.party_type = args.get('party_type')
|
||||
self.party_naming_by = frappe.db.get_value(args.get("naming_by")[0], None, args.get("naming_by")[1])
|
||||
self.get_columns()
|
||||
self.get_data(args)
|
||||
return self.columns, self.data
|
||||
|
||||
def get_data(self, args):
|
||||
self.data = []
|
||||
|
||||
self.receivables = ReceivablePayableReport(self.filters).run(args)[1]
|
||||
|
||||
self.get_party_total(args)
|
||||
|
||||
party_advance_amount = get_partywise_advanced_payment_amount(self.party_type,
|
||||
self.filters.report_date) or {}
|
||||
|
||||
for party, party_dict in iteritems(self.party_total):
|
||||
row = frappe._dict()
|
||||
|
||||
row.party = party
|
||||
if self.party_naming_by == "Naming Series":
|
||||
row.party_name = frappe.get_cached_value(self.party_type, party, [self.party_type + "_name"])
|
||||
|
||||
row.update(party_dict)
|
||||
|
||||
# Advance against party
|
||||
row.advance = party_advance_amount.get(party, 0)
|
||||
|
||||
# In AR/AP, advance shown in paid columns,
|
||||
# but in summary report advance shown in separate column
|
||||
row.paid -= row.advance
|
||||
|
||||
self.data.append(row)
|
||||
|
||||
def get_party_total(self, args):
|
||||
self.party_total = frappe._dict()
|
||||
|
||||
for d in self.receivables:
|
||||
self.init_party_total(d)
|
||||
|
||||
# Add all amount columns
|
||||
for k in list(self.party_total[d.party]):
|
||||
if k not in ["currency", "sales_person"]:
|
||||
|
||||
self.party_total[d.party][k] += d.get(k, 0.0)
|
||||
|
||||
# set territory, customer_group, sales person etc
|
||||
self.set_party_details(d)
|
||||
|
||||
def init_party_total(self, row):
|
||||
self.party_total.setdefault(row.party, frappe._dict({
|
||||
"invoiced": 0.0,
|
||||
"paid": 0.0,
|
||||
"credit_note": 0.0,
|
||||
"outstanding": 0.0,
|
||||
"range1": 0.0,
|
||||
"range2": 0.0,
|
||||
"range3": 0.0,
|
||||
"range4": 0.0,
|
||||
"range5": 0.0,
|
||||
"sales_person": []
|
||||
}))
|
||||
|
||||
def set_party_details(self, row):
|
||||
self.party_total[row.party].currency = row.currency
|
||||
|
||||
for key in ('territory', 'customer_group', 'supplier_group'):
|
||||
if row.get(key):
|
||||
self.party_total[row.party][key] = row.get(key)
|
||||
|
||||
if row.sales_person:
|
||||
self.party_total[row.party].sales_person.append(row.sales_person)
|
||||
|
||||
def get_columns(self):
|
||||
self.columns = []
|
||||
self.add_column(label=_(self.party_type), fieldname='party',
|
||||
fieldtype='Link', options=self.party_type, width=180)
|
||||
|
||||
if self.party_naming_by == "Naming Series":
|
||||
self.add_column(_('{0} Name').format(self.party_type),
|
||||
fieldname = 'party_name', fieldtype='Data')
|
||||
|
||||
credit_debit_label = "Credit Note" if self.party_type == 'Customer' else "Debit Note"
|
||||
|
||||
self.add_column(_('Advance Amount'), fieldname='advance')
|
||||
self.add_column(_('Invoiced Amount'), fieldname='invoiced')
|
||||
self.add_column(_('Paid Amount'), fieldname='paid')
|
||||
self.add_column(_(credit_debit_label), fieldname='credit_note')
|
||||
self.add_column(_('Outstanding Amount'), fieldname='outstanding')
|
||||
|
||||
self.setup_ageing_columns()
|
||||
|
||||
if self.party_type == "Customer":
|
||||
self.add_column(label=_('Territory'), fieldname='territory', fieldtype='Link',
|
||||
options='Territory')
|
||||
self.add_column(label=_('Customer Group'), fieldname='customer_group', fieldtype='Link',
|
||||
options='Customer Group')
|
||||
if self.filters.show_sales_person:
|
||||
self.add_column(label=_('Sales Person'), fieldname='sales_person', fieldtype='Data')
|
||||
else:
|
||||
self.add_column(label=_('Supplier Group'), fieldname='supplier_group', fieldtype='Link',
|
||||
options='Supplier Group')
|
||||
|
||||
self.add_column(label=_('Currency'), fieldname='currency', fieldtype='Link',
|
||||
options='Currency', width=80)
|
||||
|
||||
def setup_ageing_columns(self):
|
||||
for i, label in enumerate(["0-{range1}".format(range1=self.filters["range1"]),
|
||||
"{range1}-{range2}".format(range1=cint(self.filters["range1"])+ 1, range2=self.filters["range2"]),
|
||||
"{range2}-{range3}".format(range2=cint(self.filters["range2"])+ 1, range3=self.filters["range3"]),
|
||||
"{range3}-{range4}".format(range3=cint(self.filters["range3"])+ 1, range4=self.filters["range4"]),
|
||||
"{range4}-{above}".format(range4=cint(self.filters["range4"])+ 1, above=_("Above"))]):
|
||||
self.add_column(label=label, fieldname='range' + str(i+1))
|
||||
@@ -135,11 +135,11 @@ def get_chart_data(filters, columns, asset, liability, equity):
|
||||
|
||||
datasets = []
|
||||
if asset_data:
|
||||
datasets.append({'name':'Assets', 'values': asset_data})
|
||||
datasets.append({'name':_('Assets'), 'values': asset_data})
|
||||
if liability_data:
|
||||
datasets.append({'name':'Liabilities', 'values': liability_data})
|
||||
datasets.append({'name':_('Liabilities'), 'values': liability_data})
|
||||
if equity_data:
|
||||
datasets.append({'name':'Equity', 'values': equity_data})
|
||||
datasets.append({'name':_('Equity'), 'values': equity_data})
|
||||
|
||||
chart = {
|
||||
"data": {
|
||||
@@ -153,4 +153,4 @@ def get_chart_data(filters, columns, asset, liability, equity):
|
||||
else:
|
||||
chart["type"] = "line"
|
||||
|
||||
return chart
|
||||
return chart
|
||||
|
||||
@@ -130,7 +130,7 @@ def get_cash_flow_data(fiscal_year, companies, filters):
|
||||
section_data.append(net_profit_loss)
|
||||
|
||||
for account in cash_flow_account['account_types']:
|
||||
account_data = get_account_type_based_data(account['account_type'], companies, fiscal_year)
|
||||
account_data = get_account_type_based_data(account['account_type'], companies, fiscal_year, filters)
|
||||
account_data.update({
|
||||
"account_name": account['label'],
|
||||
"account": account['label'],
|
||||
@@ -148,12 +148,12 @@ def get_cash_flow_data(fiscal_year, companies, filters):
|
||||
|
||||
return data
|
||||
|
||||
def get_account_type_based_data(account_type, companies, fiscal_year):
|
||||
def get_account_type_based_data(account_type, companies, fiscal_year, filters):
|
||||
data = {}
|
||||
total = 0
|
||||
for company in companies:
|
||||
amount = get_account_type_based_gl_data(company,
|
||||
fiscal_year.year_start_date, fiscal_year.year_end_date, account_type)
|
||||
fiscal_year.year_start_date, fiscal_year.year_end_date, account_type, filters)
|
||||
|
||||
if amount and account_type == "Depreciation":
|
||||
amount *= -1
|
||||
|
||||
@@ -407,9 +407,12 @@ def get_cost_centers_with_children(cost_centers):
|
||||
|
||||
all_cost_centers = []
|
||||
for d in cost_centers:
|
||||
lft, rgt = frappe.db.get_value("Cost Center", d, ["lft", "rgt"])
|
||||
children = frappe.get_all("Cost Center", filters={"lft": [">=", lft], "rgt": ["<=", rgt]})
|
||||
all_cost_centers += [c.name for c in children]
|
||||
if frappe.db.exists("Cost Center", d):
|
||||
lft, rgt = frappe.db.get_value("Cost Center", d, ["lft", "rgt"])
|
||||
children = frappe.get_all("Cost Center", filters={"lft": [">=", lft], "rgt": ["<=", rgt]})
|
||||
all_cost_centers += [c.name for c in children]
|
||||
else:
|
||||
frappe.throw(_("Cost Center: {0} does not exist".format(d)))
|
||||
|
||||
return list(set(all_cost_centers))
|
||||
|
||||
|
||||
@@ -121,19 +121,11 @@ def get_gl_entries(filters):
|
||||
select_fields = """, debit, credit, debit_in_account_currency,
|
||||
credit_in_account_currency """
|
||||
|
||||
group_by_statement = ''
|
||||
order_by_statement = "order by posting_date, account"
|
||||
|
||||
if filters.get("group_by") == _("Group by Voucher"):
|
||||
order_by_statement = "order by posting_date, voucher_type, voucher_no"
|
||||
|
||||
if filters.get("group_by") == _("Group by Voucher (Consolidated)"):
|
||||
group_by_statement = "group by voucher_type, voucher_no, account, cost_center"
|
||||
|
||||
select_fields = """, sum(debit) as debit, sum(credit) as credit,
|
||||
sum(debit_in_account_currency) as debit_in_account_currency,
|
||||
sum(credit_in_account_currency) as credit_in_account_currency"""
|
||||
|
||||
if filters.get("include_default_book_entries"):
|
||||
filters['company_fb'] = frappe.db.get_value("Company",
|
||||
filters.get("company"), 'default_finance_book')
|
||||
@@ -146,11 +138,10 @@ def get_gl_entries(filters):
|
||||
against_voucher_type, against_voucher, account_currency,
|
||||
remarks, against, is_opening {select_fields}
|
||||
from `tabGL Entry`
|
||||
where company=%(company)s {conditions} {group_by_statement}
|
||||
where company=%(company)s {conditions}
|
||||
{order_by_statement}
|
||||
""".format(
|
||||
select_fields=select_fields, conditions=get_conditions(filters),
|
||||
group_by_statement=group_by_statement,
|
||||
order_by_statement=order_by_statement
|
||||
),
|
||||
filters, as_dict=1)
|
||||
@@ -187,7 +178,8 @@ def get_conditions(filters):
|
||||
if not (filters.get("account") or filters.get("party") or
|
||||
filters.get("group_by") in ["Group by Account", "Group by Party"]):
|
||||
conditions.append("posting_date >=%(from_date)s")
|
||||
conditions.append("posting_date <=%(to_date)s")
|
||||
|
||||
conditions.append("(posting_date <=%(to_date)s or is_opening = 'Yes')")
|
||||
|
||||
if filters.get("project"):
|
||||
conditions.append("project in %(project)s")
|
||||
@@ -281,6 +273,7 @@ def initialize_gle_map(gl_entries, filters):
|
||||
def get_accountwise_gle(filters, gl_entries, gle_map):
|
||||
totals = get_totals_dict()
|
||||
entries = []
|
||||
consolidated_gle = OrderedDict()
|
||||
group_by = group_by_field(filters.get('group_by'))
|
||||
|
||||
def update_value_in_dict(data, key, gle):
|
||||
@@ -306,11 +299,19 @@ def get_accountwise_gle(filters, gl_entries, gle_map):
|
||||
if filters.get("group_by") != _('Group by Voucher (Consolidated)'):
|
||||
gle_map[gle.get(group_by)].entries.append(gle)
|
||||
else:
|
||||
entries.append(gle)
|
||||
key = (gle.get("voucher_type"), gle.get("voucher_no"),
|
||||
gle.get("account"), gle.get("cost_center"))
|
||||
if key not in consolidated_gle:
|
||||
consolidated_gle.setdefault(key, gle)
|
||||
else:
|
||||
update_value_in_dict(consolidated_gle, key, gle)
|
||||
|
||||
update_value_in_dict(gle_map[gle.get(group_by)].totals, 'closing', gle)
|
||||
update_value_in_dict(totals, 'closing', gle)
|
||||
|
||||
for key, value in consolidated_gle.items():
|
||||
entries.append(value)
|
||||
|
||||
return totals, entries
|
||||
|
||||
def get_result_as_list(data, filters):
|
||||
|
||||
@@ -27,8 +27,8 @@ frappe.query_reports["Payment Period Based On Invoice Date"] = {
|
||||
fieldname:"payment_type",
|
||||
label: __("Payment Type"),
|
||||
fieldtype: "Select",
|
||||
options: "Incoming\nOutgoing",
|
||||
default: "Incoming"
|
||||
options: __("Incoming")+"\n"+__("Outgoing"),
|
||||
default: __("Incoming")
|
||||
},
|
||||
{
|
||||
"fieldname":"party_type",
|
||||
|
||||
@@ -39,8 +39,8 @@ def execute(filters=None):
|
||||
return columns, data
|
||||
|
||||
def validate_filters(filters):
|
||||
if (filters.get("payment_type") == "Incoming" and filters.get("party_type") == "Supplier") or \
|
||||
(filters.get("payment_type") == "Outgoing" and filters.get("party_type") == "Customer"):
|
||||
if (filters.get("payment_type") == _("Incoming") and filters.get("party_type") == "Supplier") or \
|
||||
(filters.get("payment_type") == _("Outgoing") and filters.get("party_type") == "Customer"):
|
||||
frappe.throw(_("{0} payment entries can not be filtered by {1}")\
|
||||
.format(filters.payment_type, filters.party_type))
|
||||
|
||||
@@ -51,7 +51,7 @@ def get_columns(filters):
|
||||
_("Party Type") + "::100",
|
||||
_("Party") + ":Dynamic Link/Party Type:140",
|
||||
_("Posting Date") + ":Date:100",
|
||||
_("Invoice") + (":Link/Purchase Invoice:130" if filters.get("payment_type") == "Outgoing" else ":Link/Sales Invoice:130"),
|
||||
_("Invoice") + (":Link/Purchase Invoice:130" if filters.get("payment_type") == _("Outgoing") else ":Link/Sales Invoice:130"),
|
||||
_("Invoice Posting Date") + ":Date:130",
|
||||
_("Payment Due Date") + ":Date:130",
|
||||
_("Debit") + ":Currency:120",
|
||||
@@ -69,7 +69,7 @@ def get_conditions(filters):
|
||||
conditions = []
|
||||
|
||||
if not filters.party_type:
|
||||
if filters.payment_type == "Outgoing":
|
||||
if filters.payment_type == _("Outgoing"):
|
||||
filters.party_type = "Supplier"
|
||||
else:
|
||||
filters.party_type = "Customer"
|
||||
@@ -101,7 +101,7 @@ def get_entries(filters):
|
||||
|
||||
def get_invoice_posting_date_map(filters):
|
||||
invoice_details = {}
|
||||
dt = "Sales Invoice" if filters.get("payment_type") == "Incoming" else "Purchase Invoice"
|
||||
dt = "Sales Invoice" if filters.get("payment_type") == _("Incoming") else "Purchase Invoice"
|
||||
for t in frappe.db.sql("select name, posting_date, due_date from `tab{0}`".format(dt), as_dict=1):
|
||||
invoice_details[t.name] = t
|
||||
|
||||
|
||||
@@ -84,7 +84,8 @@ def validate_fiscal_year(date, fiscal_year, company, label="Date", doc=None):
|
||||
throw(_("{0} '{1}' not in Fiscal Year {2}").format(label, formatdate(date), fiscal_year))
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_balance_on(account=None, date=None, party_type=None, party=None, company=None, in_account_currency=True, cost_center=None):
|
||||
def get_balance_on(account=None, date=None, party_type=None, party=None, company=None,
|
||||
in_account_currency=True, cost_center=None, ignore_account_permission=False):
|
||||
if not account and frappe.form_dict.get("account"):
|
||||
account = frappe.form_dict.get("account")
|
||||
if not date and frappe.form_dict.get("date"):
|
||||
@@ -140,7 +141,8 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, company
|
||||
|
||||
if account:
|
||||
|
||||
if not frappe.flags.ignore_account_permission:
|
||||
if not (frappe.flags.ignore_account_permission
|
||||
or ignore_account_permission):
|
||||
acc.check_permission("read")
|
||||
|
||||
if report_type == 'Profit and Loss':
|
||||
|
||||
@@ -472,7 +472,7 @@ def make_rm_stock_entry(purchase_order, rm_items):
|
||||
'from_warehouse': rm_item_data["warehouse"],
|
||||
'stock_uom': rm_item_data["stock_uom"],
|
||||
'main_item_code': rm_item_data["item_code"],
|
||||
'allow_alternative_item': item_wh[rm_item_code].get('allow_alternative_item')
|
||||
'allow_alternative_item': item_wh.get(rm_item_code, {}).get('allow_alternative_item')
|
||||
}
|
||||
}
|
||||
stock_entry.add_to_stock_entry_detail(items_dict)
|
||||
|
||||
@@ -30,7 +30,9 @@ def update_last_purchase_rate(doc, is_submit):
|
||||
# for it to be considered for latest purchase rate
|
||||
if flt(d.conversion_factor):
|
||||
last_purchase_rate = flt(d.base_rate) / flt(d.conversion_factor)
|
||||
else:
|
||||
# Check if item code is present
|
||||
# Conversion factor should not be mandatory for non itemized items
|
||||
elif d.item_code:
|
||||
frappe.throw(_("UOM Conversion factor is required in row {0}").format(d.idx))
|
||||
|
||||
# update last purchsae rate
|
||||
@@ -84,13 +86,13 @@ def get_linked_material_requests(items):
|
||||
items = json.loads(items)
|
||||
mr_list = []
|
||||
for item in items:
|
||||
material_request = frappe.db.sql("""SELECT distinct mr.name AS mr_name,
|
||||
(mr_item.qty - mr_item.ordered_qty) AS qty,
|
||||
material_request = frappe.db.sql("""SELECT distinct mr.name AS mr_name,
|
||||
(mr_item.qty - mr_item.ordered_qty) AS qty,
|
||||
mr_item.item_code AS item_code,
|
||||
mr_item.name AS mr_item
|
||||
mr_item.name AS mr_item
|
||||
FROM `tabMaterial Request` mr, `tabMaterial Request Item` mr_item
|
||||
WHERE mr.name = mr_item.parent
|
||||
AND mr_item.item_code = %(item)s
|
||||
AND mr_item.item_code = %(item)s
|
||||
AND mr.material_request_type = 'Purchase'
|
||||
AND mr.per_ordered < 99.99
|
||||
AND mr.docstatus = 1
|
||||
@@ -98,6 +100,6 @@ def get_linked_material_requests(items):
|
||||
ORDER BY mr_item.item_code ASC""",{"item": item}, as_dict=1)
|
||||
if material_request:
|
||||
mr_list.append(material_request)
|
||||
|
||||
|
||||
return mr_list
|
||||
|
||||
|
||||
@@ -1112,6 +1112,10 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name):
|
||||
.format(child_item.idx, child_item.item_code))
|
||||
else:
|
||||
child_item.rate = flt(d.get("rate"))
|
||||
if flt(child_item.price_list_rate):
|
||||
child_item.discount_percentage = flt((1 - flt(child_item.rate) / flt(child_item.price_list_rate)) * 100.0, \
|
||||
child_item.precision("discount_percentage"))
|
||||
|
||||
child_item.flags.ignore_validate_update_after_submit = True
|
||||
child_item.save()
|
||||
|
||||
|
||||
@@ -395,7 +395,9 @@ class BuyingController(StockController):
|
||||
def set_qty_as_per_stock_uom(self):
|
||||
for d in self.get("items"):
|
||||
if d.meta.get_field("stock_qty"):
|
||||
if not d.conversion_factor:
|
||||
# Check if item code is present
|
||||
# Conversion factor should not be mandatory for non itemized items
|
||||
if not d.conversion_factor and d.item_code:
|
||||
frappe.throw(_("Row {0}: Conversion Factor is mandatory").format(d.idx))
|
||||
d.stock_qty = flt(d.qty) * flt(d.conversion_factor)
|
||||
|
||||
@@ -715,7 +717,7 @@ def get_items_from_bom(item_code, bom, exploded_item=1):
|
||||
where
|
||||
t2.parent = t1.name and t1.item = %s
|
||||
and t1.docstatus = 1 and t1.is_active = 1 and t1.name = %s
|
||||
and t2.item_code = t3.name and t3.is_stock_item = 1""".format(doctype),
|
||||
and t2.item_code = t3.name""".format(doctype),
|
||||
(item_code, bom), as_dict=1)
|
||||
|
||||
if not bom_items:
|
||||
|
||||
@@ -371,7 +371,7 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters):
|
||||
|
||||
return frappe.db.sql("""select tabAccount.name from `tabAccount`
|
||||
where (tabAccount.report_type = "Profit and Loss"
|
||||
or tabAccount.account_type in ("Expense Account", "Fixed Asset", "Temporary", "Asset Received But Not Billed"))
|
||||
or tabAccount.account_type in ("Expense Account", "Fixed Asset", "Temporary", "Asset Received But Not Billed", "Capital Work in Progress"))
|
||||
and tabAccount.is_group=0
|
||||
and tabAccount.docstatus!=2
|
||||
and tabAccount.{key} LIKE %(txt)s
|
||||
|
||||
@@ -18,34 +18,31 @@ def validate_return(doc):
|
||||
validate_returned_items(doc)
|
||||
|
||||
def validate_return_against(doc):
|
||||
filters = {"doctype": doc.doctype, "docstatus": 1, "company": doc.company}
|
||||
if doc.meta.get_field("customer") and doc.customer:
|
||||
filters["customer"] = doc.customer
|
||||
elif doc.meta.get_field("supplier") and doc.supplier:
|
||||
filters["supplier"] = doc.supplier
|
||||
|
||||
if not frappe.db.exists(filters):
|
||||
if not frappe.db.exists(doc.doctype, doc.return_against):
|
||||
frappe.throw(_("Invalid {0}: {1}")
|
||||
.format(doc.meta.get_label("return_against"), doc.return_against))
|
||||
else:
|
||||
ref_doc = frappe.get_doc(doc.doctype, doc.return_against)
|
||||
|
||||
# validate posting date time
|
||||
return_posting_datetime = "%s %s" % (doc.posting_date, doc.get("posting_time") or "00:00:00")
|
||||
ref_posting_datetime = "%s %s" % (ref_doc.posting_date, ref_doc.get("posting_time") or "00:00:00")
|
||||
party_type = "customer" if doc.doctype in ("Sales Invoice", "Delivery Note") else "supplier"
|
||||
|
||||
if get_datetime(return_posting_datetime) < get_datetime(ref_posting_datetime):
|
||||
frappe.throw(_("Posting timestamp must be after {0}").format(format_datetime(ref_posting_datetime)))
|
||||
if ref_doc.company == doc.company and ref_doc.get(party_type) == doc.get(party_type) and ref_doc.docstatus == 1:
|
||||
# validate posting date time
|
||||
return_posting_datetime = "%s %s" % (doc.posting_date, doc.get("posting_time") or "00:00:00")
|
||||
ref_posting_datetime = "%s %s" % (ref_doc.posting_date, ref_doc.get("posting_time") or "00:00:00")
|
||||
|
||||
# validate same exchange rate
|
||||
if doc.conversion_rate != ref_doc.conversion_rate:
|
||||
frappe.throw(_("Exchange Rate must be same as {0} {1} ({2})")
|
||||
.format(doc.doctype, doc.return_against, ref_doc.conversion_rate))
|
||||
if get_datetime(return_posting_datetime) < get_datetime(ref_posting_datetime):
|
||||
frappe.throw(_("Posting timestamp must be after {0}").format(format_datetime(ref_posting_datetime)))
|
||||
|
||||
# validate update stock
|
||||
if doc.doctype == "Sales Invoice" and doc.update_stock and not ref_doc.update_stock:
|
||||
frappe.throw(_("'Update Stock' can not be checked because items are not delivered via {0}")
|
||||
.format(doc.return_against))
|
||||
# validate same exchange rate
|
||||
if doc.conversion_rate != ref_doc.conversion_rate:
|
||||
frappe.throw(_("Exchange Rate must be same as {0} {1} ({2})")
|
||||
.format(doc.doctype, doc.return_against, ref_doc.conversion_rate))
|
||||
|
||||
# validate update stock
|
||||
if doc.doctype == "Sales Invoice" and doc.update_stock and not ref_doc.update_stock:
|
||||
frappe.throw(_("'Update Stock' can not be checked because items are not delivered via {0}")
|
||||
.format(doc.return_against))
|
||||
|
||||
def validate_returned_items(doc):
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
|
||||
@@ -45,6 +45,7 @@ class SellingController(StockController):
|
||||
self.set_gross_profit()
|
||||
set_default_income_account_for_item(self)
|
||||
self.set_customer_address()
|
||||
self.validate_for_duplicate_items()
|
||||
|
||||
def set_missing_values(self, for_validate=False):
|
||||
|
||||
@@ -381,6 +382,34 @@ class SellingController(StockController):
|
||||
if self.get(address_field):
|
||||
self.set(address_display_field, get_address_display(self.get(address_field)))
|
||||
|
||||
def validate_for_duplicate_items(self):
|
||||
check_list, chk_dupl_itm = [], []
|
||||
if cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")):
|
||||
return
|
||||
|
||||
for d in self.get('items'):
|
||||
if self.doctype == "Sales Invoice":
|
||||
e = [d.item_code, d.description, d.warehouse, d.sales_order or d.delivery_note, d.batch_no or '']
|
||||
f = [d.item_code, d.description, d.sales_order or d.delivery_note]
|
||||
elif self.doctype == "Delivery Note":
|
||||
e = [d.item_code, d.description, d.warehouse, d.against_sales_order or d.against_sales_invoice, d.batch_no or '']
|
||||
f = [d.item_code, d.description, d.against_sales_order or d.against_sales_invoice]
|
||||
elif self.doctype in ["Sales Order", "Quotation"]:
|
||||
e = [d.item_code, d.description, d.warehouse, '']
|
||||
f = [d.item_code, d.description]
|
||||
|
||||
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1:
|
||||
if e in check_list:
|
||||
frappe.throw(_("Note: Item {0} entered multiple times").format(d.item_code))
|
||||
else:
|
||||
check_list.append(e)
|
||||
else:
|
||||
if f in chk_dupl_itm:
|
||||
frappe.throw(_("Note: Item {0} entered multiple times").format(d.item_code))
|
||||
else:
|
||||
chk_dupl_itm.append(f)
|
||||
|
||||
|
||||
def validate_items(self):
|
||||
# validate items to see if they have is_sales_item enabled
|
||||
from erpnext.controllers.buying_controller import validate_item_type
|
||||
|
||||
@@ -77,7 +77,7 @@ class calculate_taxes_and_totals(object):
|
||||
|
||||
item.net_rate = item.rate
|
||||
|
||||
if not item.qty and self.doc.is_return:
|
||||
if not item.qty and self.doc.get("is_return"):
|
||||
item.amount = flt(-1 * item.rate, item.precision("amount"))
|
||||
else:
|
||||
item.amount = flt(item.rate * item.qty, item.precision("amount"))
|
||||
|
||||
@@ -21,42 +21,45 @@ def get_list_context(context=None):
|
||||
|
||||
def get_transaction_list(doctype, txt=None, filters=None, limit_start=0, limit_page_length=20, order_by="modified"):
|
||||
user = frappe.session.user
|
||||
key = None
|
||||
ignore_permissions = False
|
||||
|
||||
if not filters: filters = []
|
||||
|
||||
if doctype == 'Supplier Quotation':
|
||||
filters.append((doctype, "docstatus", "<", 2))
|
||||
filters.append((doctype, 'docstatus', '<', 2))
|
||||
else:
|
||||
filters.append((doctype, "docstatus", "=", 1))
|
||||
filters.append((doctype, 'docstatus', '=', 1))
|
||||
|
||||
if (user != "Guest" and is_website_user()) or doctype == 'Request for Quotation':
|
||||
if (user != 'Guest' and is_website_user()) or doctype == 'Request for Quotation':
|
||||
parties_doctype = 'Request for Quotation Supplier' if doctype == 'Request for Quotation' else doctype
|
||||
# find party for this contact
|
||||
customers, suppliers = get_customers_suppliers(parties_doctype, user)
|
||||
|
||||
if not customers and not suppliers: return []
|
||||
|
||||
key, parties = get_party_details(customers, suppliers)
|
||||
|
||||
if doctype == 'Request for Quotation':
|
||||
return rfq_transaction_list(parties_doctype, doctype, parties, limit_start, limit_page_length)
|
||||
|
||||
filters.append((doctype, key, "in", parties))
|
||||
|
||||
if key:
|
||||
return post_process(doctype, get_list_for_transactions(doctype, txt,
|
||||
filters=filters, fields="name",limit_start=limit_start,
|
||||
limit_page_length=limit_page_length,ignore_permissions=True,
|
||||
order_by="modified desc"))
|
||||
if customers:
|
||||
if doctype == 'Quotation':
|
||||
filters.append(('quotation_to', '=', 'Customer'))
|
||||
filters.append(('party_name', 'in', customers))
|
||||
else:
|
||||
filters.append(('customer', 'in', customers))
|
||||
elif suppliers:
|
||||
filters.append(('supplier', 'in', suppliers))
|
||||
else:
|
||||
return []
|
||||
|
||||
return post_process(doctype, get_list_for_transactions(doctype, txt, filters, limit_start, limit_page_length,
|
||||
fields="name", order_by="modified desc"))
|
||||
if doctype == 'Request for Quotation':
|
||||
parties = customers or suppliers
|
||||
return rfq_transaction_list(parties_doctype, doctype, parties, limit_start, limit_page_length)
|
||||
|
||||
# Since customers and supplier do not have direct access to internal doctypes
|
||||
ignore_permissions = True
|
||||
|
||||
transactions = get_list_for_transactions(doctype, txt, filters, limit_start, limit_page_length,
|
||||
fields='name', ignore_permissions=ignore_permissions, order_by='modified desc')
|
||||
|
||||
return post_process(doctype, transactions)
|
||||
|
||||
def get_list_for_transactions(doctype, txt, filters, limit_start, limit_page_length=20,
|
||||
ignore_permissions=False,fields=None, order_by=None):
|
||||
ignore_permissions=False, fields=None, order_by=None):
|
||||
""" Get List of transactions like Invoices, Orders """
|
||||
from frappe.www.list import get_list
|
||||
meta = frappe.get_meta(doctype)
|
||||
@@ -77,22 +80,12 @@ def get_list_for_transactions(doctype, txt, filters, limit_start, limit_page_len
|
||||
|
||||
if or_filters:
|
||||
for r in frappe.get_list(doctype, fields=fields,filters=filters, or_filters=or_filters,
|
||||
limit_start=limit_start, limit_page_length=limit_page_length,
|
||||
limit_start=limit_start, limit_page_length=limit_page_length,
|
||||
ignore_permissions=ignore_permissions, order_by=order_by):
|
||||
data.append(r)
|
||||
|
||||
return data
|
||||
|
||||
def get_party_details(customers, suppliers):
|
||||
if customers:
|
||||
key, parties = "customer", customers
|
||||
elif suppliers:
|
||||
key, parties = "supplier", suppliers
|
||||
else:
|
||||
key, parties = "customer", []
|
||||
|
||||
return key, parties
|
||||
|
||||
def rfq_transaction_list(parties_doctype, doctype, parties, limit_start, limit_page_length):
|
||||
data = frappe.db.sql("""select distinct parent as name, supplier from `tab{doctype}`
|
||||
where supplier = '{supplier}' and docstatus=1 order by modified desc limit {start}, {len}""".
|
||||
@@ -130,38 +123,56 @@ def get_customers_suppliers(doctype, user):
|
||||
suppliers = []
|
||||
meta = frappe.get_meta(doctype)
|
||||
|
||||
customer_field_name = get_customer_field_name(doctype)
|
||||
|
||||
has_customer_field = meta.has_field(customer_field_name)
|
||||
has_supplier_field = meta.has_field('supplier')
|
||||
|
||||
if has_common(["Supplier", "Customer"], frappe.get_roles(user)):
|
||||
contacts = frappe.db.sql("""
|
||||
select
|
||||
select
|
||||
`tabContact`.email_id,
|
||||
`tabDynamic Link`.link_doctype,
|
||||
`tabDynamic Link`.link_name
|
||||
from
|
||||
from
|
||||
`tabContact`, `tabDynamic Link`
|
||||
where
|
||||
`tabContact`.name=`tabDynamic Link`.parent and `tabContact`.email_id =%s
|
||||
""", user, as_dict=1)
|
||||
customers = [c.link_name for c in contacts if c.link_doctype == 'Customer'] \
|
||||
if meta.get_field("customer") else None
|
||||
suppliers = [c.link_name for c in contacts if c.link_doctype == 'Supplier'] \
|
||||
if meta.get_field("supplier") else None
|
||||
customers = [c.link_name for c in contacts if c.link_doctype == 'Customer']
|
||||
suppliers = [c.link_name for c in contacts if c.link_doctype == 'Supplier']
|
||||
elif frappe.has_permission(doctype, 'read', user=user):
|
||||
customers = [customer.name for customer in frappe.get_list("Customer")] \
|
||||
if meta.get_field("customer") else None
|
||||
suppliers = [supplier.name for supplier in frappe.get_list("Customer")] \
|
||||
if meta.get_field("supplier") else None
|
||||
customer_list = frappe.get_list("Customer")
|
||||
customers = suppliers = [customer.name for customer in customer_list]
|
||||
|
||||
return customers, suppliers
|
||||
return customers if has_customer_field else None, \
|
||||
suppliers if has_supplier_field else None
|
||||
|
||||
def has_website_permission(doc, ptype, user, verbose=False):
|
||||
doctype = doc.doctype
|
||||
customers, suppliers = get_customers_suppliers(doctype, user)
|
||||
if customers:
|
||||
return frappe.get_all(doctype, filters=[(doctype, "customer", "in", customers),
|
||||
(doctype, "name", "=", doc.name)]) and True or False
|
||||
return frappe.db.exists(doctype, get_customer_filter(doc, customers))
|
||||
elif suppliers:
|
||||
fieldname = 'suppliers' if doctype == 'Request for Quotation' else 'supplier'
|
||||
return frappe.get_all(doctype, filters=[(doctype, fieldname, "in", suppliers),
|
||||
(doctype, "name", "=", doc.name)]) and True or False
|
||||
return frappe.db.exists(doctype, filters={
|
||||
'name': doc.name,
|
||||
fieldname: ["in", suppliers]
|
||||
})
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_customer_filter(doc, customers):
|
||||
doctype = doc.doctype
|
||||
filters = frappe._dict()
|
||||
filters.name = doc.name
|
||||
filters[get_customer_field_name(doctype)] = ['in', customers]
|
||||
if doctype == 'Quotation':
|
||||
filters.quotation_to = 'Customer'
|
||||
return filters
|
||||
|
||||
def get_customer_field_name(doctype):
|
||||
if doctype == 'Quotation':
|
||||
return 'party_name'
|
||||
else:
|
||||
return 'customer'
|
||||
@@ -88,7 +88,7 @@ def get_status(start_date, end_date):
|
||||
end_date = getdate(end_date)
|
||||
now_date = getdate(nowdate())
|
||||
|
||||
return "Active" if start_date < now_date < end_date else "Inactive"
|
||||
return "Active" if start_date <= now_date <= end_date else "Inactive"
|
||||
|
||||
|
||||
def update_status_for_contracts():
|
||||
|
||||
@@ -1460,7 +1460,7 @@
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "fa fa-info-sign",
|
||||
"icon": "fa fa-info-circle",
|
||||
"idx": 195,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 1,
|
||||
@@ -54,6 +55,7 @@
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "eval:doc.client_id && doc.client_secret && doc.redirect_url",
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "application_settings",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@@ -87,6 +89,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "client_id",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
@@ -120,6 +123,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "redirect_url",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
@@ -153,6 +157,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "token_endpoint",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
@@ -185,6 +190,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "application_column_break",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@@ -217,6 +223,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "client_secret",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
@@ -250,6 +257,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "com.intuit.quickbooks.accounting",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "scope",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
@@ -284,6 +292,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "https://quickbooks.api.intuit.com/v3",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "api_endpoint",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
@@ -316,6 +325,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "authorization_settings",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@@ -349,6 +359,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "https://appcenter.intuit.com/connect/oauth2",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "authorization_endpoint",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
@@ -381,6 +392,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "refresh_token",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
@@ -413,6 +425,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "code",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
@@ -445,6 +458,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "authorization_column_break",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@@ -476,6 +490,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "authorization_url",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
@@ -508,6 +523,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "access_token",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
@@ -540,6 +556,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "quickbooks_company_id",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
@@ -572,6 +589,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "company_settings",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 1,
|
||||
@@ -604,6 +622,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -637,6 +656,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "default_shipping_account",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
@@ -670,6 +690,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "default_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
@@ -703,6 +724,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "company_column_break",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@@ -734,6 +756,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "default_cost_center",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
@@ -767,6 +790,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "undeposited_funds_account",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
@@ -804,7 +828,7 @@
|
||||
"issingle": 1,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-10-17 03:12:53.506229",
|
||||
"modified": "2019-08-07 05:53:00.920316",
|
||||
"modified_by": "Administrator",
|
||||
"module": "ERPNext Integrations",
|
||||
"name": "QuickBooks Migrator",
|
||||
@@ -834,7 +858,7 @@
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 1,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 0,
|
||||
|
||||
@@ -6,7 +6,7 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cint, cstr, getdate
|
||||
from frappe.utils import cint, cstr, getdate, flt
|
||||
import dateutil
|
||||
from frappe.model.naming import set_name_by_naming_series
|
||||
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account,get_income_account,send_registration_sms
|
||||
@@ -64,7 +64,7 @@ class Patient(Document):
|
||||
def invoice_patient_registration(self):
|
||||
frappe.db.set_value("Patient", self.name, "disabled", 0)
|
||||
send_registration_sms(self)
|
||||
if(frappe.get_value("Healthcare Settings", None, "registration_fee")>0):
|
||||
if(flt(frappe.get_value("Healthcare Settings", None, "registration_fee"))>0):
|
||||
company = frappe.defaults.get_user_default('company')
|
||||
if not company:
|
||||
company = frappe.db.get_value("Global Defaults", None, "default_company")
|
||||
|
||||
@@ -64,13 +64,20 @@ class EmployeeAdvance(Document):
|
||||
|
||||
def update_claimed_amount(self):
|
||||
claimed_amount = frappe.db.sql("""
|
||||
select sum(ifnull(allocated_amount, 0))
|
||||
from `tabExpense Claim Advance`
|
||||
where employee_advance = %s and docstatus=1 and allocated_amount > 0
|
||||
SELECT sum(ifnull(allocated_amount, 0))
|
||||
FROM `tabExpense Claim Advance` eca, `tabExpense Claim` ec
|
||||
WHERE
|
||||
eca.employee_advance = %s
|
||||
AND ec.approval_status="Approved"
|
||||
AND ec.name = eca.parent
|
||||
AND ec.docstatus=1
|
||||
AND eca.allocated_amount > 0
|
||||
""", self.name)[0][0] or 0
|
||||
|
||||
if claimed_amount:
|
||||
frappe.db.set_value("Employee Advance", self.name, "claimed_amount", flt(claimed_amount))
|
||||
frappe.db.set_value("Employee Advance", self.name, "claimed_amount", flt(claimed_amount))
|
||||
self.reload()
|
||||
self.set_status()
|
||||
frappe.db.set_value("Employee Advance", self.name, "status", self.status)
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_due_advance_amount(employee, posting_date):
|
||||
|
||||
@@ -178,7 +178,7 @@ frappe.ui.form.on("Expense Claim", {
|
||||
refresh: function(frm) {
|
||||
frm.trigger("toggle_fields");
|
||||
|
||||
if(frm.doc.docstatus == 1) {
|
||||
if(frm.doc.docstatus === 1 && frm.doc.approval_status !== "Rejected") {
|
||||
frm.add_custom_button(__('Accounting Ledger'), function() {
|
||||
frappe.route_options = {
|
||||
voucher_no: frm.doc.name,
|
||||
@@ -188,8 +188,7 @@ frappe.ui.form.on("Expense Claim", {
|
||||
frappe.set_route("query-report", "General Ledger");
|
||||
}, __("View"));
|
||||
}
|
||||
|
||||
if (frm.doc.docstatus===1
|
||||
if (frm.doc.docstatus===1 && !cint(frm.doc.is_paid)
|
||||
&& (cint(frm.doc.total_amount_reimbursed) < cint(frm.doc.total_sanctioned_amount))
|
||||
&& frappe.model.can_create("Payment Entry")) {
|
||||
frm.add_custom_button(__('Payment'),
|
||||
|
||||
@@ -30,11 +30,11 @@ class LoanApplication(Document):
|
||||
monthly_interest_rate = flt(self.rate_of_interest) / (12 *100)
|
||||
if monthly_interest_rate:
|
||||
min_repayment_amount = self.loan_amount*monthly_interest_rate
|
||||
if self.repayment_amount - min_repayment_amount < 0:
|
||||
if (self.repayment_amount - min_repayment_amount) <= 0:
|
||||
frappe.throw(_("Repayment Amount must be greater than " \
|
||||
+ str(flt(min_repayment_amount, 2))))
|
||||
self.repayment_periods = math.ceil(math.log(self.repayment_amount) -
|
||||
math.log(self.repayment_amount - min_repayment_amount) /(math.log(1 + monthly_interest_rate)))
|
||||
self.repayment_periods = math.ceil((math.log(self.repayment_amount) -
|
||||
math.log(self.repayment_amount - min_repayment_amount)) /(math.log(1 + monthly_interest_rate)))
|
||||
else:
|
||||
self.repayment_periods = self.loan_amount / self.repayment_amount
|
||||
|
||||
@@ -58,10 +58,13 @@ def make_loan(source_name, target_doc = None):
|
||||
doclist = get_mapped_doc("Loan Application", source_name, {
|
||||
"Loan Application": {
|
||||
"doctype": "Loan",
|
||||
"field_map": {
|
||||
"repayment_amount": "monthly_repayment_amount"
|
||||
},
|
||||
"validation": {
|
||||
"docstatus": ["=", 1]
|
||||
}
|
||||
}
|
||||
}, target_doc)
|
||||
|
||||
return doclist
|
||||
return doclist
|
||||
|
||||
@@ -69,7 +69,7 @@ frappe.ui.form.on('Payroll Entry', {
|
||||
},
|
||||
|
||||
add_context_buttons: function(frm) {
|
||||
if(frm.doc.salary_slips_submitted) {
|
||||
if(frm.doc.salary_slips_submitted || (frm.doc.__onload && frm.doc.__onload.submitted_ss)) {
|
||||
frm.events.add_bank_entry_button(frm);
|
||||
} else if(frm.doc.salary_slips_created) {
|
||||
frm.add_custom_button(__("Submit Salary Slip"), function() {
|
||||
|
||||
@@ -13,14 +13,13 @@ from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
||||
|
||||
class PayrollEntry(Document):
|
||||
def onload(self):
|
||||
if not self.docstatus==1:
|
||||
if not self.docstatus==1 or self.salary_slips_submitted:
|
||||
return
|
||||
|
||||
# check if salary slips were manually submitted
|
||||
entries = frappe.db.count("Salary Slip", {'payroll_entry': self.name, 'docstatus': 1}, ['name'])
|
||||
if cint(entries) == len(self.employees) and not self.salary_slips_submitted:
|
||||
self.db_set("salary_slips_submitted", 1)
|
||||
self.reload()
|
||||
if cint(entries) == len(self.employees):
|
||||
self.set_onload("submitted_ss", True)
|
||||
|
||||
def on_submit(self):
|
||||
self.create_salary_slips()
|
||||
@@ -423,7 +422,6 @@ def get_start_end_dates(payroll_frequency, start_date=None, company=None):
|
||||
'start_date': start_date, 'end_date': end_date
|
||||
})
|
||||
|
||||
|
||||
def get_frequency_kwargs(frequency_name):
|
||||
frequency_dict = {
|
||||
'monthly': {'months': 1},
|
||||
|
||||
@@ -97,7 +97,7 @@ class ProductionPlan(Document):
|
||||
self.get_mr_items()
|
||||
|
||||
def get_so_items(self):
|
||||
so_list = [d.sales_order for d in self.sales_orders if d.sales_order]
|
||||
so_list = [d.sales_order for d in self.get("sales_orders", []) if d.sales_order]
|
||||
if not so_list:
|
||||
msgprint(_("Please enter Sales Orders in the above table"))
|
||||
return []
|
||||
@@ -132,7 +132,7 @@ class ProductionPlan(Document):
|
||||
self.calculate_total_planned_qty()
|
||||
|
||||
def get_mr_items(self):
|
||||
mr_list = [d.material_request for d in self.material_requests if d.material_request]
|
||||
mr_list = [d.material_request for d in self.get("material_requests", []) if d.material_request]
|
||||
if not mr_list:
|
||||
msgprint(_("Please enter Material Requests in the above table"))
|
||||
return []
|
||||
|
||||
@@ -165,6 +165,7 @@ class Project(Document):
|
||||
|
||||
task.run_method("validate")
|
||||
task.db_update()
|
||||
task.notify_update()
|
||||
else:
|
||||
task.save(ignore_permissions = True)
|
||||
task_names.append(task.name)
|
||||
|
||||
@@ -147,6 +147,15 @@ frappe.ui.form.on("Timesheet Detail", {
|
||||
calculate_time_and_amount(frm);
|
||||
},
|
||||
|
||||
task: (frm, cdt, cdn) => {
|
||||
let row = frm.selected_doc;
|
||||
if (row.task) {
|
||||
frappe.db.get_value("Task", row.task, "project", (r) => {
|
||||
frappe.model.set_value(cdt, cdn, "project", r.project);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
from_time: function(frm, cdt, cdn) {
|
||||
calculate_end_time(frm, cdt, cdn);
|
||||
},
|
||||
@@ -200,9 +209,6 @@ frappe.ui.form.on("Timesheet Detail", {
|
||||
},
|
||||
|
||||
activity_type: function(frm, cdt, cdn) {
|
||||
frm.script_manager.copy_from_first_row('time_logs', frm.selected_doc,
|
||||
'project');
|
||||
|
||||
frappe.call({
|
||||
method: "erpnext.projects.doctype.timesheet.timesheet.get_activity_cost",
|
||||
args: {
|
||||
|
||||
@@ -148,12 +148,17 @@ class Timesheet(Document):
|
||||
def validate_time_logs(self):
|
||||
for data in self.get('time_logs'):
|
||||
self.validate_overlap(data)
|
||||
self.validate_task_project()
|
||||
|
||||
def validate_overlap(self, data):
|
||||
settings = frappe.get_single('Projects Settings')
|
||||
self.validate_overlap_for("user", data, self.user, settings.ignore_user_time_overlap)
|
||||
self.validate_overlap_for("employee", data, self.employee, settings.ignore_employee_time_overlap)
|
||||
|
||||
def validate_task_project(self):
|
||||
for log in self.time_logs:
|
||||
log.project = log.project or frappe.db.get_value("Task", log.task, "project")
|
||||
|
||||
def validate_overlap_for(self, fieldname, args, value, ignore_validation=False):
|
||||
if not value or ignore_validation:
|
||||
return
|
||||
|
||||
@@ -391,7 +391,8 @@ erpnext.buying.get_items_from_product_bundle = function(frm) {
|
||||
company: frm.doc.company,
|
||||
is_subcontracted: frm.doc.is_subcontracted,
|
||||
transaction_date: frm.doc.transaction_date || frm.doc.posting_date,
|
||||
ignore_pricing_rule: frm.doc.ignore_pricing_rule
|
||||
ignore_pricing_rule: frm.doc.ignore_pricing_rule,
|
||||
doctype: frm.doc.doctype
|
||||
}
|
||||
},
|
||||
freeze: true,
|
||||
|
||||
@@ -129,6 +129,8 @@ def download_zip(files, output_filename):
|
||||
|
||||
def get_invoice_summary(items, taxes):
|
||||
summary_data = frappe._dict()
|
||||
|
||||
applied_tax_row_ids = []
|
||||
for tax in taxes:
|
||||
#Include only VAT charges.
|
||||
if tax.charge_type == "Actual":
|
||||
@@ -153,7 +155,9 @@ def get_invoice_summary(items, taxes):
|
||||
net_amount=reference_row.tax_amount,
|
||||
taxable_amount=reference_row.tax_amount,
|
||||
item_tax_rate={tax.account_head: tax.rate},
|
||||
charges=True
|
||||
charges=True,
|
||||
type="Actual",
|
||||
tax_row_name=tax.name
|
||||
)
|
||||
)
|
||||
|
||||
@@ -165,13 +169,21 @@ def get_invoice_summary(items, taxes):
|
||||
item_tax_rate = json.loads(item.item_tax_rate)
|
||||
|
||||
if item_tax_rate and tax.account_head in item_tax_rate:
|
||||
if (item.get("tax_row_name")
|
||||
and item.get("tax_row_name") in applied_tax_row_ids):
|
||||
continue
|
||||
|
||||
key = cstr(item_tax_rate[tax.account_head])
|
||||
if key not in summary_data:
|
||||
summary_data.setdefault(key, {"tax_amount": 0.0, "taxable_amount": 0.0,
|
||||
"tax_exemption_reason": "", "tax_exemption_law": ""})
|
||||
|
||||
if item.get("type") and item.get("type") == "Actual":
|
||||
applied_tax_row_ids.append(item.get("tax_row_name"))
|
||||
|
||||
summary_data[key]["tax_amount"] += item.tax_amount
|
||||
summary_data[key]["taxable_amount"] += item.net_amount
|
||||
|
||||
if key == "0.0":
|
||||
summary_data[key]["tax_exemption_reason"] = tax.tax_exemption_reason
|
||||
summary_data[key]["tax_exemption_law"] = tax.tax_exemption_law
|
||||
|
||||
@@ -362,7 +362,7 @@ def get_customer_primary_contact(doctype, txt, searchfield, start, page_len, fil
|
||||
return frappe.db.sql("""
|
||||
select `tabContact`.name from `tabContact`, `tabDynamic Link`
|
||||
where `tabContact`.name = `tabDynamic Link`.parent and `tabDynamic Link`.link_name = %(customer)s
|
||||
and `tabDynamic Link`.link_doctype = 'Customer' and `tabContact`.is_primary_contact = 1
|
||||
and `tabDynamic Link`.link_doctype = 'Customer'
|
||||
and `tabContact`.name like %(txt)s
|
||||
""", {
|
||||
'customer': customer,
|
||||
@@ -374,7 +374,7 @@ def get_customer_primary_address(doctype, txt, searchfield, start, page_len, fil
|
||||
return frappe.db.sql("""
|
||||
select `tabAddress`.name from `tabAddress`, `tabDynamic Link`
|
||||
where `tabAddress`.name = `tabDynamic Link`.parent and `tabDynamic Link`.link_name = %(customer)s
|
||||
and `tabDynamic Link`.link_doctype = 'Customer' and `tabAddress`.is_primary_address = 1
|
||||
and `tabDynamic Link`.link_doctype = 'Customer'
|
||||
and `tabAddress`.name like %(txt)s
|
||||
""", {
|
||||
'customer': customer,
|
||||
|
||||
@@ -262,27 +262,44 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
var fields = [
|
||||
{fieldtype:'Table', fieldname: 'items',
|
||||
description: __('Select BOM and Qty for Production'),
|
||||
fields: [
|
||||
{fieldtype:'Read Only', fieldname:'item_code',
|
||||
label: __('Item Code'), in_list_view:1},
|
||||
{fieldtype:'Link', fieldname:'bom', options: 'BOM', reqd: 1,
|
||||
label: __('Select BOM'), in_list_view:1, get_query: function(doc) {
|
||||
return {filters: {item: doc.item_code}};
|
||||
}},
|
||||
{fieldtype:'Float', fieldname:'pending_qty', reqd: 1,
|
||||
label: __('Qty'), in_list_view:1},
|
||||
{fieldtype:'Data', fieldname:'sales_order_item', reqd: 1,
|
||||
label: __('Sales Order Item'), hidden:1}
|
||||
],
|
||||
data: r.message,
|
||||
get_data: function() {
|
||||
return r.message
|
||||
const fields = [{
|
||||
label: 'Items',
|
||||
fieldtype: 'Table',
|
||||
fieldname: 'items',
|
||||
description: __('Select BOM and Qty for Production'),
|
||||
fields: [{
|
||||
fieldtype: 'Read Only',
|
||||
fieldname: 'item_code',
|
||||
label: __('Item Code'),
|
||||
in_list_view: 1
|
||||
}, {
|
||||
fieldtype: 'Link',
|
||||
fieldname: 'bom',
|
||||
options: 'BOM',
|
||||
reqd: 1,
|
||||
label: __('Select BOM'),
|
||||
in_list_view: 1,
|
||||
get_query: function (doc) {
|
||||
return { filters: { item: doc.item_code } };
|
||||
}
|
||||
}, {
|
||||
fieldtype: 'Float',
|
||||
fieldname: 'pending_qty',
|
||||
reqd: 1,
|
||||
label: __('Qty'),
|
||||
in_list_view: 1
|
||||
}, {
|
||||
fieldtype: 'Data',
|
||||
fieldname: 'sales_order_item',
|
||||
reqd: 1,
|
||||
label: __('Sales Order Item'),
|
||||
hidden: 1
|
||||
}],
|
||||
data: r.message,
|
||||
get_data: () => {
|
||||
return r.message
|
||||
}
|
||||
]
|
||||
}]
|
||||
var d = new frappe.ui.Dialog({
|
||||
title: __('Select Items to Manufacture'),
|
||||
fields: fields,
|
||||
|
||||
@@ -69,9 +69,7 @@ class SalesOrder(SellingController):
|
||||
frappe.msgprint(_("Warning: Sales Order {0} already exists against Customer's Purchase Order {1}").format(so[0][0], self.po_no))
|
||||
|
||||
def validate_for_items(self):
|
||||
check_list = []
|
||||
for d in self.get('items'):
|
||||
check_list.append(cstr(d.item_code))
|
||||
|
||||
# used for production plan
|
||||
d.transaction_date = self.transaction_date
|
||||
@@ -80,13 +78,6 @@ class SalesOrder(SellingController):
|
||||
where item_code = %s and warehouse = %s", (d.item_code, d.warehouse))
|
||||
d.projected_qty = tot_avail_qty and flt(tot_avail_qty[0][0]) or 0
|
||||
|
||||
# check for same entry multiple times
|
||||
unique_chk_list = set(check_list)
|
||||
if len(unique_chk_list) != len(check_list) and \
|
||||
not cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")):
|
||||
frappe.msgprint(_("Same item has been entered multiple times"),
|
||||
title=_("Warning"), indicator='orange')
|
||||
|
||||
def product_bundle_has_stock_item(self, product_bundle):
|
||||
"""Returns true if product bundle has stock item"""
|
||||
ret = len(frappe.db.sql("""select i.name from tabItem i, `tabProduct Bundle Item` pbi
|
||||
|
||||
@@ -4,7 +4,7 @@ frappe.provide('erpnext.pos');
|
||||
frappe.pages['point-of-sale'].on_page_load = function(wrapper) {
|
||||
frappe.ui.make_app_page({
|
||||
parent: wrapper,
|
||||
title: 'Point of Sale',
|
||||
title: __("Point of Sale"),
|
||||
single_column: true
|
||||
});
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ def get_items(start, page_length, price_list, item_group, search_value="", pos_p
|
||||
batch_no = data.get("batch_no") if data.get("batch_no") else ""
|
||||
barcode = data.get("barcode") if data.get("barcode") else ""
|
||||
|
||||
item_code, condition = get_conditions(item_code, serial_no, batch_no, barcode)
|
||||
condition = get_conditions(item_code, serial_no, batch_no, barcode)
|
||||
|
||||
if pos_profile:
|
||||
condition += get_item_group_condition(pos_profile)
|
||||
@@ -38,58 +38,60 @@ def get_items(start, page_length, price_list, item_group, search_value="", pos_p
|
||||
# locate function is used to sort by closest match from the beginning of the value
|
||||
|
||||
|
||||
if display_items_in_stock == 0:
|
||||
res = frappe.db.sql("""select i.name as item_code, i.item_name, i.image as item_image, i.idx as idx,
|
||||
i.is_stock_item, item_det.price_list_rate, item_det.currency
|
||||
from `tabItem` i LEFT JOIN
|
||||
(select item_code, price_list_rate, currency from
|
||||
`tabItem Price` where price_list=%(price_list)s) item_det
|
||||
ON
|
||||
(item_det.item_code=i.name or item_det.item_code=i.variant_of)
|
||||
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} order by idx desc limit {start}, {page_length}""".format(start=start,page_length=page_length,lft=lft, rgt=rgt, condition=condition),
|
||||
{
|
||||
'item_code': item_code,
|
||||
'price_list': price_list
|
||||
} , as_dict=1)
|
||||
result = []
|
||||
|
||||
res = {
|
||||
'items': res
|
||||
}
|
||||
items_data = frappe.db.sql(""" SELECT name as item_code,
|
||||
item_name, image as item_image, idx as idx,is_stock_item
|
||||
FROM
|
||||
`tabItem`
|
||||
WHERE
|
||||
disabled = 0 and has_variants = 0 and is_sales_item = 1
|
||||
and item_group in (select name from `tabItem Group` where lft >= {lft} and rgt <= {rgt})
|
||||
and {condition} order by idx desc limit {start}, {page_length}"""
|
||||
.format(
|
||||
start=start, page_length=page_length,
|
||||
lft=lft, rgt=rgt,
|
||||
condition=condition
|
||||
), as_dict=1)
|
||||
|
||||
elif display_items_in_stock == 1:
|
||||
query = """select i.name as item_code, i.item_name, i.image as item_image, i.idx as idx,
|
||||
i.is_stock_item, item_det.price_list_rate, item_det.currency
|
||||
from `tabItem` i LEFT JOIN
|
||||
(select item_code, price_list_rate, currency from
|
||||
`tabItem Price` where price_list=%(price_list)s) item_det
|
||||
ON
|
||||
(item_det.item_code=i.name or item_det.item_code=i.variant_of) INNER JOIN"""
|
||||
if items_data:
|
||||
items = [d.item_code for d in items_data]
|
||||
item_prices_data = frappe.get_all("Item Price",
|
||||
fields = ["item_code", "price_list_rate", "currency"],
|
||||
filters = {'price_list': price_list, 'item_code': ['in', items]})
|
||||
|
||||
if warehouse is not None:
|
||||
query = query + """ (select item_code,actual_qty from `tabBin` where warehouse=%(warehouse)s and actual_qty > 0 group by item_code) item_se"""
|
||||
else:
|
||||
query = query + """ (select item_code,sum(actual_qty) as actual_qty from `tabBin` group by item_code) item_se"""
|
||||
item_prices, bin_data = {}, {}
|
||||
for d in item_prices_data:
|
||||
item_prices[d.item_code] = d
|
||||
|
||||
res = frappe.db.sql(query + """
|
||||
ON
|
||||
((item_se.item_code=i.name or item_det.item_code=i.variant_of) and item_se.actual_qty>0)
|
||||
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} order by idx desc limit {start}, {page_length}""".format
|
||||
(start=start,page_length=page_length,lft=lft, rgt=rgt, condition=condition),
|
||||
{
|
||||
'item_code': item_code,
|
||||
'price_list': price_list,
|
||||
'warehouse': warehouse
|
||||
} , as_dict=1)
|
||||
|
||||
res = {
|
||||
'items': res
|
||||
}
|
||||
if display_items_in_stock:
|
||||
filters = {'actual_qty': [">", 0], 'item_code': ['in', items]}
|
||||
|
||||
if warehouse:
|
||||
filters['warehouse'] = warehouse
|
||||
|
||||
bin_data = frappe._dict(
|
||||
frappe.get_all("Bin", fields = ["item_code", "sum(actual_qty) as actual_qty"],
|
||||
filters = filters, group_by = "item_code")
|
||||
)
|
||||
|
||||
for item in items_data:
|
||||
row = {}
|
||||
|
||||
row.update(item)
|
||||
item_price = item_prices.get(item.item_code) or {}
|
||||
row.update({
|
||||
'price_list_rate': item_price.get('price_list_rate'),
|
||||
'currency': item_price.get('currency'),
|
||||
'actual_qty': bin_data.get('actual_qty')
|
||||
})
|
||||
|
||||
result.append(row)
|
||||
|
||||
res = {
|
||||
'items': result
|
||||
}
|
||||
|
||||
if serial_no:
|
||||
res.update({
|
||||
@@ -129,18 +131,16 @@ def search_serial_or_batch_or_barcode_number(search_value):
|
||||
|
||||
def get_conditions(item_code, serial_no, batch_no, barcode):
|
||||
if serial_no or batch_no or barcode:
|
||||
return frappe.db.escape(item_code), "i.name = %(item_code)s"
|
||||
return "name = '{0}'".format(frappe.db.escape(item_code))
|
||||
|
||||
condition = """(i.name like %(item_code)s
|
||||
or i.item_name like %(item_code)s)"""
|
||||
|
||||
return '%%%s%%'%(frappe.db.escape(item_code)), condition
|
||||
return """(name like '{item_code}'
|
||||
or item_name like '{item_code}')""".format(item_code = frappe.db.escape('%' + item_code + '%'))
|
||||
|
||||
def get_item_group_condition(pos_profile):
|
||||
cond = "and 1=1"
|
||||
item_groups = get_item_groups(pos_profile)
|
||||
if item_groups:
|
||||
cond = "and i.item_group in (%s)"%(', '.join(['%s']*len(item_groups)))
|
||||
cond = "and item_group in (%s)"%(', '.join(['%s']*len(item_groups)))
|
||||
|
||||
return cond % tuple(item_groups)
|
||||
|
||||
|
||||
@@ -40,6 +40,16 @@ class Analytics(object):
|
||||
"fieldtype": "Data",
|
||||
"width": 140
|
||||
})
|
||||
|
||||
if self.filters.tree_type == "Item":
|
||||
self.columns.append({
|
||||
"label": _("UOM"),
|
||||
"fieldname": 'stock_uom',
|
||||
"fieldtype": "Link",
|
||||
"options": "UOM",
|
||||
"width": 100
|
||||
})
|
||||
|
||||
for end_date in self.periodic_daterange:
|
||||
period = self.get_period(end_date)
|
||||
self.columns.append({
|
||||
@@ -107,7 +117,7 @@ class Analytics(object):
|
||||
value_field = 'qty'
|
||||
|
||||
self.entries = frappe.db.sql("""
|
||||
select i.item_code as entity, i.item_name as entity_name, i.{value_field} as value_field, s.{date_field}
|
||||
select i.item_code as entity, i.item_name as entity_name, i.stock_uom, i.{value_field} as value_field, s.{date_field}
|
||||
from `tab{doctype} Item` i , `tab{doctype}` s
|
||||
where s.name = i.parent and i.docstatus = 1 and s.company = %s
|
||||
and s.{date_field} between %s and %s
|
||||
@@ -176,6 +186,10 @@ class Analytics(object):
|
||||
total += amount
|
||||
|
||||
row["total"] = total
|
||||
|
||||
if self.filters.tree_type == "Item":
|
||||
row["stock_uom"] = period_data.get("stock_uom")
|
||||
|
||||
self.data.append(row)
|
||||
|
||||
def get_rows_by_group(self):
|
||||
@@ -210,6 +224,9 @@ class Analytics(object):
|
||||
self.entity_periodic_data.setdefault(d.entity, frappe._dict()).setdefault(period, 0.0)
|
||||
self.entity_periodic_data[d.entity][period] += flt(d.value_field)
|
||||
|
||||
if self.filters.tree_type == "Item":
|
||||
self.entity_periodic_data[d.entity]['stock_uom'] = d.stock_uom
|
||||
|
||||
def get_period(self, posting_date):
|
||||
if self.filters.range == 'Weekly':
|
||||
period = "Week " + str(posting_date.isocalendar()[1]) + " " + str(posting_date.year)
|
||||
|
||||
@@ -254,7 +254,7 @@ class Company(NestedSet):
|
||||
def set_mode_of_payment_account(self):
|
||||
cash = frappe.db.get_value('Mode of Payment', {'type': 'Cash'}, 'name')
|
||||
if cash and self.default_cash_account \
|
||||
and not frappe.db.get_value('Mode of Payment Account', {'company': self.name}):
|
||||
and not frappe.db.get_value('Mode of Payment Account', {'company': self.name, 'parent': cash}):
|
||||
mode_of_payment = frappe.get_doc('Mode of Payment', cash)
|
||||
mode_of_payment.append('accounts', {
|
||||
'company': self.name,
|
||||
|
||||
@@ -58,7 +58,7 @@ class GlobalDefaults(Document):
|
||||
|
||||
# Make property setters to hide rounded total fields
|
||||
for doctype in ("Quotation", "Sales Order", "Sales Invoice", "Delivery Note",
|
||||
"Supplier Quotation", "Purchase Order"):
|
||||
"Supplier Quotation", "Purchase Order", "Purchase Invoice"):
|
||||
make_property_setter(doctype, "base_rounded_total", "hidden", self.disable_rounded_total, "Check")
|
||||
make_property_setter(doctype, "base_rounded_total", "print_hide", 1, "Check")
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import urllib
|
||||
import copy
|
||||
from frappe.utils import nowdate, cint, cstr
|
||||
from frappe.utils.nestedset import NestedSet
|
||||
@@ -12,6 +11,7 @@ from frappe.website.render import clear_cache
|
||||
from frappe.website.doctype.website_slideshow.website_slideshow import get_slideshow
|
||||
from erpnext.shopping_cart.product_info import set_product_info_for_website
|
||||
from erpnext.utilities.product import get_qty_in_stock
|
||||
from six.moves.urllib.parse import quote
|
||||
|
||||
class ItemGroup(NestedSet, WebsiteGenerator):
|
||||
nsm_parent_field = 'parent_item_group'
|
||||
@@ -166,7 +166,7 @@ def get_item_for_list_in_html(context):
|
||||
# add missing absolute link in files
|
||||
# user may forget it during upload
|
||||
if (context.get("website_image") or "").startswith("files/"):
|
||||
context["website_image"] = "/" + urllib.quote(context["website_image"])
|
||||
context["website_image"] = "/" + quote(context["website_image"])
|
||||
|
||||
context["show_availability_status"] = cint(frappe.db.get_single_value('Products Settings',
|
||||
'show_availability_status'))
|
||||
@@ -216,4 +216,4 @@ def get_item_group_defaults(item, company):
|
||||
row.pop("name")
|
||||
return row
|
||||
|
||||
return frappe._dict()
|
||||
return frappe._dict()
|
||||
|
||||
@@ -202,13 +202,12 @@ def _get_cart_quotation(party=None):
|
||||
if quotation:
|
||||
qdoc = frappe.get_doc("Quotation", quotation[0].name)
|
||||
else:
|
||||
[company, price_list] = frappe.db.get_value("Shopping Cart Settings", None, ["company", "price_list"])
|
||||
company = frappe.db.get_value("Shopping Cart Settings", None, ["company"])
|
||||
qdoc = frappe.get_doc({
|
||||
"doctype": "Quotation",
|
||||
"naming_series": get_shopping_cart_settings().quotation_series or "QTN-CART-",
|
||||
"quotation_to": party.doctype,
|
||||
"company": company,
|
||||
"selling_price_list": price_list,
|
||||
"order_type": "Shopping Cart",
|
||||
"status": "Draft",
|
||||
"docstatus": 0,
|
||||
@@ -288,19 +287,20 @@ def set_price_list_and_rate(quotation, cart_settings):
|
||||
|
||||
def _set_price_list(quotation, cart_settings):
|
||||
"""Set price list based on customer or shopping cart default"""
|
||||
if quotation.selling_price_list:
|
||||
return
|
||||
from erpnext.accounts.party import get_default_price_list
|
||||
|
||||
# check if customer price list exists
|
||||
selling_price_list = None
|
||||
if quotation.party_name:
|
||||
from erpnext.accounts.party import get_default_price_list
|
||||
selling_price_list = get_default_price_list(frappe.get_doc("Customer", quotation.party_name))
|
||||
selling_price_list = frappe.db.get_value('Customer', quotation.party_name, 'default_price_list')
|
||||
|
||||
# else check for territory based price list
|
||||
if not selling_price_list:
|
||||
selling_price_list = cart_settings.price_list
|
||||
|
||||
if not selling_price_list and quotation.party_name:
|
||||
selling_price_list = get_default_price_list(frappe.get_doc("Customer", quotation.party_name))
|
||||
|
||||
quotation.selling_price_list = selling_price_list
|
||||
|
||||
def set_taxes(quotation, cart_settings):
|
||||
|
||||
@@ -171,7 +171,7 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
|
||||
});
|
||||
|
||||
if(!from_sales_invoice) {
|
||||
this.frm.add_custom_button(__('Invoice'), function() { me.make_sales_invoice() },
|
||||
this.frm.add_custom_button(__('Sales Invoice'), function() { me.make_sales_invoice() },
|
||||
__("Make"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +111,6 @@ class DeliveryNote(SellingController):
|
||||
self.so_required()
|
||||
self.validate_proj_cust()
|
||||
self.check_close_sales_order("against_sales_order")
|
||||
self.validate_for_items()
|
||||
self.validate_warehouse()
|
||||
self.validate_uom_is_integer("stock_uom", "stock_qty")
|
||||
self.validate_uom_is_integer("uom", "qty")
|
||||
@@ -165,25 +164,6 @@ class DeliveryNote(SellingController):
|
||||
if not res:
|
||||
frappe.throw(_("Customer {0} does not belong to project {1}").format(self.customer, self.project))
|
||||
|
||||
def validate_for_items(self):
|
||||
check_list, chk_dupl_itm = [], []
|
||||
if cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")):
|
||||
return
|
||||
|
||||
for d in self.get('items'):
|
||||
e = [d.item_code, d.description, d.warehouse, d.against_sales_order or d.against_sales_invoice, d.batch_no or '']
|
||||
f = [d.item_code, d.description, d.against_sales_order or d.against_sales_invoice]
|
||||
|
||||
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1:
|
||||
if e in check_list:
|
||||
frappe.msgprint(_("Note: Item {0} entered multiple times").format(d.item_code))
|
||||
else:
|
||||
check_list.append(e)
|
||||
else:
|
||||
if f in chk_dupl_itm:
|
||||
frappe.msgprint(_("Note: Item {0} entered multiple times").format(d.item_code))
|
||||
else:
|
||||
chk_dupl_itm.append(f)
|
||||
|
||||
def validate_warehouse(self):
|
||||
super(DeliveryNote, self).validate_warehouse()
|
||||
|
||||
@@ -701,6 +701,7 @@ def create_delivery_note(**args):
|
||||
"qty": args.qty or 1,
|
||||
"rate": args.rate or 100,
|
||||
"conversion_factor": 1.0,
|
||||
"allow_zero_valuation_rate": args.allow_zero_valuation_rate or 1,
|
||||
"expense_account": "Cost of Goods Sold - _TC",
|
||||
"cost_center": args.cost_center or "_Test Cost Center - _TC",
|
||||
"serial_no": args.serial_no,
|
||||
|
||||
@@ -340,7 +340,7 @@ def get_directions(route, optimize):
|
||||
try:
|
||||
directions = maps_client.directions(**directions_data)
|
||||
except Exception as e:
|
||||
frappe.throw(_(e.message))
|
||||
frappe.throw(_(e))
|
||||
|
||||
return directions[0] if directions else False
|
||||
|
||||
|
||||
@@ -124,6 +124,7 @@ class Item(WebsiteGenerator):
|
||||
self.validate_item_defaults()
|
||||
self.update_defaults_from_item_group()
|
||||
self.validate_stock_for_has_batch_and_has_serial()
|
||||
self.update_show_in_website()
|
||||
|
||||
if not self.get("__islocal"):
|
||||
self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group")
|
||||
@@ -437,6 +438,10 @@ class Item(WebsiteGenerator):
|
||||
|
||||
[self.remove(d) for d in to_remove]
|
||||
|
||||
def update_show_in_website(self):
|
||||
if self.disabled:
|
||||
self.show_in_website = False
|
||||
|
||||
def update_template_tables(self):
|
||||
template = frappe.get_doc("Item", self.variant_of)
|
||||
|
||||
|
||||
@@ -39,11 +39,12 @@ cur_frm.fields_dict['item_code'].get_query = function(doc, cdt, cdn) {
|
||||
query: "erpnext.stock.doctype.quality_inspection.quality_inspection.item_query",
|
||||
filters: {
|
||||
"from": doctype,
|
||||
"parent": doc.reference_name
|
||||
"parent": doc.reference_name,
|
||||
"inspection_type": doc.inspection_type
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Serial No based on item_code
|
||||
cur_frm.fields_dict['item_serial_no'].get_query = function(doc, cdt, cdn) {
|
||||
|
||||
@@ -63,10 +63,12 @@ def item_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
mcond = get_match_cond(filters["from"])
|
||||
cond, qi_condition = "", "and (quality_inspection is null or quality_inspection = '')"
|
||||
|
||||
if filters.get('from') in ['Purchase Invoice Item', 'Purchase Receipt Item']:
|
||||
if filters.get('from') in ['Purchase Invoice Item', 'Purchase Receipt Item']\
|
||||
and filters.get("inspection_type") != "In Process":
|
||||
cond = """and item_code in (select name from `tabItem` where
|
||||
inspection_required_before_purchase = 1)"""
|
||||
elif filters.get('from') in ['Sales Invoice Item', 'Delivery Note Item']:
|
||||
elif filters.get('from') in ['Sales Invoice Item', 'Delivery Note Item']\
|
||||
and filters.get("inspection_type") != "In Process":
|
||||
cond = """and item_code in (select name from `tabItem` where
|
||||
inspection_required_before_delivery = 1)"""
|
||||
elif filters.get('from') == 'Stock Entry Detail':
|
||||
|
||||
@@ -22,6 +22,11 @@ frappe.pages['stock-balance'].on_page_load = function(wrapper) {
|
||||
label: __('Item'),
|
||||
fieldtype:'Link',
|
||||
options:'Item',
|
||||
get_query: function() {
|
||||
return {
|
||||
query: "erpnext.controllers.queries.item_query"
|
||||
}
|
||||
},
|
||||
change: function() {
|
||||
page.item_dashboard.start = 0;
|
||||
page.item_dashboard.refresh();
|
||||
|
||||
@@ -8,7 +8,12 @@ frappe.query_reports["Item Price Stock"] = {
|
||||
"fieldname":"item_code",
|
||||
"label": __("Item"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Item"
|
||||
"options": "Item",
|
||||
"get_query": function() {
|
||||
return {
|
||||
query: "erpnext.controllers.queries.item_query"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,12 @@ frappe.query_reports["Stock Ageing"] = {
|
||||
"fieldname":"item_code",
|
||||
"label": __("Item"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Item"
|
||||
"options": "Item",
|
||||
"get_query": function() {
|
||||
return {
|
||||
query: "erpnext.controllers.queries.item_query"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname":"brand",
|
||||
@@ -37,4 +42,4 @@ frappe.query_reports["Stock Ageing"] = {
|
||||
"options": "Brand"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,11 @@ frappe.query_reports["Stock Analytics"] = {
|
||||
label: __("Item"),
|
||||
fieldtype: "Link",
|
||||
options:"Item",
|
||||
get_query: function() {
|
||||
return {
|
||||
query: "erpnext.controllers.queries.item_query"
|
||||
}
|
||||
},
|
||||
default: "",
|
||||
},
|
||||
{
|
||||
|
||||
@@ -178,10 +178,7 @@ class update_entries_after(object):
|
||||
# rounding as per precision
|
||||
self.stock_value = flt(self.stock_value, self.precision)
|
||||
|
||||
if self.prev_stock_value < 0 and self.stock_value >= 0 and sle.voucher_type != 'Stock Reconciliation':
|
||||
stock_value_difference = sle.actual_qty * self.valuation_rate
|
||||
else:
|
||||
stock_value_difference = self.stock_value - self.prev_stock_value
|
||||
stock_value_difference = self.stock_value - self.prev_stock_value
|
||||
|
||||
self.prev_stock_value = self.stock_value
|
||||
|
||||
@@ -224,9 +221,9 @@ class update_entries_after(object):
|
||||
elif actual_qty < 0:
|
||||
# In case of delivery/stock issue, get average purchase rate
|
||||
# of serial nos of current entry
|
||||
stock_value_change = -1 * flt(frappe.db.sql("""select sum(purchase_rate)
|
||||
from `tabSerial No` where name in (%s)""" % (", ".join(["%s"]*len(serial_no))),
|
||||
tuple(serial_no))[0][0])
|
||||
stock_value_change = -1 * flt(frappe.get_all("Serial No",
|
||||
fields=["sum(purchase_rate)"],
|
||||
filters = {'name': ('in', serial_no)}, as_list=1)[0][0])
|
||||
|
||||
new_stock_qty = self.qty_after_transaction + actual_qty
|
||||
|
||||
@@ -457,16 +454,22 @@ def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no,
|
||||
|
||||
last_valuation_rate = frappe.db.sql("""select valuation_rate
|
||||
from `tabStock Ledger Entry`
|
||||
where item_code = %s and warehouse = %s
|
||||
and valuation_rate >= 0
|
||||
order by posting_date desc, posting_time desc, name desc limit 1""", (item_code, warehouse))
|
||||
where
|
||||
item_code = %s
|
||||
AND warehouse = %s
|
||||
AND valuation_rate >= 0
|
||||
AND NOT (voucher_no = %s AND voucher_type = %s)
|
||||
order by posting_date desc, posting_time desc, name desc limit 1""", (item_code, warehouse, voucher_no, voucher_type))
|
||||
|
||||
if not last_valuation_rate:
|
||||
# Get valuation rate from last sle for the item against any warehouse
|
||||
last_valuation_rate = frappe.db.sql("""select valuation_rate
|
||||
from `tabStock Ledger Entry`
|
||||
where item_code = %s and valuation_rate > 0
|
||||
order by posting_date desc, posting_time desc, name desc limit 1""", item_code)
|
||||
where
|
||||
item_code = %s
|
||||
AND valuation_rate > 0
|
||||
AND NOT(voucher_no = %s AND voucher_type = %s)
|
||||
order by posting_date desc, posting_time desc, name desc limit 1""", (item_code, voucher_no, voucher_type))
|
||||
|
||||
if last_valuation_rate:
|
||||
return flt(last_valuation_rate[0][0]) # as there is previous records, it might come with zero rate
|
||||
|
||||
@@ -15,7 +15,7 @@ def get_stock_value_from_bin(warehouse=None, item_code=None):
|
||||
values = {}
|
||||
conditions = ""
|
||||
if warehouse:
|
||||
conditions += """ and warehouse in (
|
||||
conditions += """ and `tabBin`.warehouse in (
|
||||
select w2.name from `tabWarehouse` w1
|
||||
join `tabWarehouse` w2 on
|
||||
w1.name = %(warehouse)s
|
||||
@@ -25,11 +25,12 @@ def get_stock_value_from_bin(warehouse=None, item_code=None):
|
||||
values['warehouse'] = warehouse
|
||||
|
||||
if item_code:
|
||||
conditions += " and item_code = %(item_code)s"
|
||||
conditions += " and `tabBin`.item_code = %(item_code)s"
|
||||
|
||||
values['item_code'] = item_code
|
||||
|
||||
query = "select sum(stock_value) from `tabBin` where 1 = 1 %s" % conditions
|
||||
query = """select sum(stock_value) from `tabBin`, `tabItem` where 1 = 1
|
||||
and `tabItem`.name = `tabBin`.item_code and ifnull(`tabItem`.disabled, 0) = 0 %s""" % conditions
|
||||
|
||||
stock_value = frappe.db.sql(query, values)
|
||||
|
||||
|
||||
@@ -51,14 +51,14 @@ erpnext.SupportAnalytics = frappe.views.GridReportWithPlot.extend({
|
||||
// add Opening, Closing, Totals rows
|
||||
// if filtered by account and / or voucher
|
||||
var me = this;
|
||||
var total_tickets = {name:"All Tickets", "id": "all-tickets",
|
||||
var total_tickets = {name:__("All Tickets"), "id": "all-tickets",
|
||||
checked:true};
|
||||
var days_to_close = {name:"Days to Close", "id":"days-to-close",
|
||||
var days_to_close = {name:__("Days to Close"), "id":"days-to-close",
|
||||
checked:false};
|
||||
var total_closed = {};
|
||||
var hours_to_close = {name:"Hours to Close", "id":"hours-to-close",
|
||||
var hours_to_close = {name:__("Hours to Close"), "id":"hours-to-close",
|
||||
checked:false};
|
||||
var hours_to_respond = {name:"Hours to Respond", "id":"hours-to-respond",
|
||||
var hours_to_respond = {name:__("Hours to Respond"), "id":"hours-to-respond",
|
||||
checked:false};
|
||||
var total_responded = {};
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ frappe.ready(function() {
|
||||
.html(r.message.product_info.price.formatted_price_sales_uom + "<div style='font-size: small'>\
|
||||
(" + r.message.product_info.price.formatted_price + " / " + r.message.product_info.uom + ")</div>");
|
||||
|
||||
if(r.message.product_info.in_stock==0) {
|
||||
if(r.message.product_info.in_stock==0 && r.message.cart_settings.show_stock_availability) {
|
||||
$(".item-stock").html("<div style='color: red'> <i class='fa fa-close'></i> {{ _("Not in stock") }}</div>");
|
||||
}
|
||||
else if(r.message.product_info.in_stock==1) {
|
||||
|
||||
@@ -5,8 +5,7 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import formatdate
|
||||
from erpnext.controllers.website_list_for_contact import (get_customers_suppliers,
|
||||
get_party_details)
|
||||
from erpnext.controllers.website_list_for_contact import get_customers_suppliers
|
||||
|
||||
def get_context(context):
|
||||
context.no_cache = 1
|
||||
@@ -23,8 +22,8 @@ def get_supplier():
|
||||
doctype = frappe.form_dict.doctype
|
||||
parties_doctype = 'Request for Quotation Supplier' if doctype == 'Request for Quotation' else doctype
|
||||
customers, suppliers = get_customers_suppliers(parties_doctype, frappe.session.user)
|
||||
key, parties = get_party_details(customers, suppliers)
|
||||
return parties[0] if key == 'supplier' else ''
|
||||
|
||||
return suppliers[0] if suppliers else ''
|
||||
|
||||
def check_supplier_has_docname_access(supplier):
|
||||
status = True
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{%- macro render_discount_amount(doc) -%}
|
||||
{%- if doc.discount_amount -%}
|
||||
<div class="row">
|
||||
<div class="col-xs-5 {%- if doc._align_labels_right %} text-right{%- endif -%}">
|
||||
<div class="col-xs-5 {%- if doc.align_labels_right %} text-right{%- endif -%}">
|
||||
<label>{{ _(doc.meta.get_label('discount_amount')) }}</label></div>
|
||||
<div class="col-xs-7 text-right">
|
||||
- {{ doc.get_formatted("discount_amount", doc) }}
|
||||
@@ -19,7 +19,7 @@
|
||||
{%- for charge in data -%}
|
||||
{%- if (charge.tax_amount or doc.flags.print_taxes_with_zero_amount) and (not charge.included_in_print_rate or doc.flags.show_inclusive_tax_in_print) -%}
|
||||
<div class="row">
|
||||
<div class="col-xs-5 {%- if doc._align_labels_right %} text-right{%- endif -%}">
|
||||
<div class="col-xs-5 {%- if doc.align_labels_right %} text-right{%- endif -%}">
|
||||
<label>{{ charge.get_formatted("description") }}</label></div>
|
||||
<div class="col-xs-7 text-right">
|
||||
{{ frappe.format_value(frappe.utils.flt(charge.tax_amount),
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<div class="row">
|
||||
{% if doc.flags.show_inclusive_tax_in_print %}
|
||||
<div class="col-xs-5 {%- if doc._align_labels_right %} text-right{%- endif -%}">
|
||||
<div class="col-xs-5 {%- if doc.align_labels_right %} text-right{%- endif -%}">
|
||||
<label>{{ _("Total (Without Tax)") }}</label></div>
|
||||
<div class="col-xs-7 text-right">
|
||||
{{ doc.get_formatted("net_total", doc) }}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="col-xs-5 {%- if doc._align_labels_right %} text-right{%- endif -%}">
|
||||
<div class="col-xs-5 {%- if doc.align_labels_right %} text-right{%- endif -%}">
|
||||
<label>{{ _(doc.meta.get_label('total')) }}</label></div>
|
||||
<div class="col-xs-7 text-right">
|
||||
{{ doc.get_formatted("total", doc) }}
|
||||
|
||||
Reference in New Issue
Block a user