Fixed merge conflict
This commit is contained in:
commit
32a62a02df
@ -29,7 +29,7 @@ install:
|
||||
- cp -r $TRAVIS_BUILD_DIR/test_sites/test_site ~/frappe-bench/sites/
|
||||
|
||||
before_script:
|
||||
- wget http://chromedriver.storage.googleapis.com/2.27/chromedriver_linux64.zip
|
||||
- wget http://chromedriver.storage.googleapis.com/2.32/chromedriver_linux64.zip
|
||||
- unzip chromedriver_linux64.zip
|
||||
- sudo apt-get install libnss3
|
||||
- sudo apt-get --only-upgrade install google-chrome-stable
|
||||
|
@ -74,6 +74,36 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "due_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Due Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@ -718,7 +748,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-08-03 12:40:09.611951",
|
||||
"modified": "2017-08-10 18:06:44.904081",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "GL Entry",
|
||||
|
@ -185,8 +185,39 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
||||
})
|
||||
},
|
||||
|
||||
due_date_options_cache: {},
|
||||
|
||||
reference_name: function(doc, cdt, cdn) {
|
||||
var d = frappe.get_doc(cdt, cdn);
|
||||
var me = this;
|
||||
|
||||
const get_invoice_due_dates = invoice_name => {
|
||||
const options = this.due_date_options_cache[invoice_name];
|
||||
const input = $(cur_frm.fields_dict["accounts"].wrapper).find("select[data-fieldname=reference_due_date]");
|
||||
|
||||
if (options) {
|
||||
input.empty();
|
||||
input.add_options(options);
|
||||
frappe.model.set_value(cdt, cdn, "reference_due_date", options[0]);
|
||||
}
|
||||
else {
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_invoice_due_dates",
|
||||
args: {name: invoice_name},
|
||||
callback: function(r) {
|
||||
const options = [];
|
||||
$.each(r.message, function(key, value) {
|
||||
options.push(value.due_date);
|
||||
});
|
||||
input.empty();
|
||||
input.add_options(options);
|
||||
frappe.model.set_value(cdt, cdn, "reference_due_date", options[0]);
|
||||
me.due_date_options_cache[d.reference_name] = options;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if(d.reference_name) {
|
||||
if (d.reference_type==="Purchase Invoice" && !flt(d.debit)) {
|
||||
this.get_outstanding('Purchase Invoice', d.reference_name, doc.company, d);
|
||||
@ -197,6 +228,9 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
||||
if (d.reference_type==="Journal Entry" && !flt(d.credit) && !flt(d.debit)) {
|
||||
this.get_outstanding('Journal Entry', d.reference_name, doc.company, d);
|
||||
}
|
||||
if( in_list(["Sales Invoice", "Purchase Invoice"]), d.reference_type) {
|
||||
get_invoice_due_dates(d.reference_name);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -436,7 +436,8 @@ class JournalEntry(AccountsController):
|
||||
"against_voucher": d.reference_name,
|
||||
"remarks": self.remark,
|
||||
"cost_center": d.cost_center,
|
||||
"project": d.project
|
||||
"project": d.project,
|
||||
"due_date": d.reference_due_date
|
||||
})
|
||||
)
|
||||
|
||||
@ -898,3 +899,14 @@ def get_average_exchange_rate(account):
|
||||
exchange_rate = bank_balance_in_company_currency / bank_balance_in_account_currency
|
||||
|
||||
return exchange_rate
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_invoice_due_dates(name):
|
||||
result = frappe.get_list(
|
||||
doctype='GL Entry', group_by='name, due_date',
|
||||
filters={'voucher_no': name, "ifnull(due_date, '')": ('!=', '')},
|
||||
fields=['due_date'], distinct=True
|
||||
)
|
||||
|
||||
return result
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "hash",
|
||||
@ -13,6 +14,7 @@
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 1,
|
||||
"collapsible": 0,
|
||||
@ -46,6 +48,7 @@
|
||||
"width": "250px"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -75,6 +78,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -106,6 +110,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -141,6 +146,7 @@
|
||||
"width": "180px"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -168,6 +174,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -197,6 +204,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -226,6 +234,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -256,6 +265,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -287,6 +297,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -317,6 +328,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -345,6 +357,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -374,6 +387,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -402,6 +416,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 1,
|
||||
"collapsible": 0,
|
||||
@ -432,6 +447,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 1,
|
||||
"collapsible": 0,
|
||||
@ -464,6 +480,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -491,6 +508,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 1,
|
||||
"collapsible": 0,
|
||||
@ -521,6 +539,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 1,
|
||||
"collapsible": 0,
|
||||
@ -553,6 +572,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -581,6 +601,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -611,6 +632,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -641,6 +663,39 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.reference_type&&!in_list(doc.reference_type, ['Expense Claim', 'Asset', 'Employee Loan'])",
|
||||
"fieldname": "reference_due_date",
|
||||
"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": "Reference Due Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -671,6 +726,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -698,6 +754,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -729,6 +786,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -759,17 +817,17 @@
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-03-02 05:02:10.102039",
|
||||
"modified": "2017-08-30 08:44:54.295493",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Journal Entry Account",
|
||||
|
@ -8,14 +8,16 @@ from frappe import _, scrub, ValidationError
|
||||
from frappe.utils import flt, comma_or, nowdate
|
||||
from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on
|
||||
from erpnext.accounts.party import get_party_account
|
||||
from erpnext.accounts.doctype.journal_entry.journal_entry \
|
||||
import get_average_exchange_rate, get_default_bank_cash_account
|
||||
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
|
||||
from erpnext.setup.utils import get_exchange_rate
|
||||
from erpnext.accounts.general_ledger import make_gl_entries
|
||||
from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amount
|
||||
from erpnext.controllers.accounts_controller import AccountsController
|
||||
|
||||
class InvalidPaymentEntry(ValidationError): pass
|
||||
|
||||
class InvalidPaymentEntry(ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class PaymentEntry(AccountsController):
|
||||
def setup_party_account_field(self):
|
||||
@ -69,10 +71,9 @@ class PaymentEntry(AccountsController):
|
||||
def validate_duplicate_entry(self):
|
||||
reference_names = []
|
||||
for d in self.get("references"):
|
||||
if (d.reference_doctype, d.reference_name) in reference_names:
|
||||
if (d.reference_doctype, d.reference_name, d.due_date) in reference_names:
|
||||
frappe.throw(_("Row #{0}: Duplicate entry in References {1} {2}").format(d.idx, d.reference_doctype, d.reference_name))
|
||||
reference_names.append((d.reference_doctype, d.reference_name))
|
||||
|
||||
reference_names.append((d.reference_doctype, d.reference_name, d.due_date))
|
||||
|
||||
def validate_allocated_amount(self):
|
||||
for d in self.get("references"):
|
||||
@ -80,7 +81,6 @@ class PaymentEntry(AccountsController):
|
||||
if flt(d.allocated_amount) > flt(d.outstanding_amount):
|
||||
frappe.throw(_("Row #{0}: Allocated Amount cannot be greater than outstanding amount.").format(d.idx))
|
||||
|
||||
|
||||
def delink_advance_entry_references(self):
|
||||
for reference in self.references:
|
||||
if reference.reference_doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||
@ -128,7 +128,6 @@ class PaymentEntry(AccountsController):
|
||||
|
||||
self.set_missing_ref_details()
|
||||
|
||||
|
||||
def set_missing_ref_details(self):
|
||||
for d in self.get("references"):
|
||||
if d.allocated_amount:
|
||||
@ -295,7 +294,7 @@ class PaymentEntry(AccountsController):
|
||||
|
||||
def set_difference_amount(self):
|
||||
base_unallocated_amount = flt(self.unallocated_amount) * (flt(self.source_exchange_rate)
|
||||
if self.payment_type=="Receive" else flt(self.target_exchange_rate))
|
||||
if self.payment_type == "Receive" else flt(self.target_exchange_rate))
|
||||
|
||||
base_party_amount = flt(self.base_total_allocated_amount) + flt(base_unallocated_amount)
|
||||
|
||||
@ -413,7 +412,8 @@ class PaymentEntry(AccountsController):
|
||||
gle = party_gl_dict.copy()
|
||||
gle.update({
|
||||
"against_voucher_type": d.reference_doctype,
|
||||
"against_voucher": d.reference_name
|
||||
"against_voucher": d.reference_name,
|
||||
"due_date": d.due_date
|
||||
})
|
||||
|
||||
allocated_amount_in_company_currency = flt(flt(d.allocated_amount) * flt(d.exchange_rate),
|
||||
@ -505,12 +505,10 @@ def get_outstanding_reference_documents(args):
|
||||
company_currency = frappe.db.get_value("Company", args.get("company"), "default_currency")
|
||||
|
||||
# Get negative outstanding sales /purchase invoices
|
||||
total_field = "base_grand_total" if party_account_currency == company_currency else "grand_total"
|
||||
|
||||
negative_outstanding_invoices = []
|
||||
if (args.get("party_type") != "Student"):
|
||||
if args.get("party_type") not in ["Student", "Employee"]:
|
||||
negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"),
|
||||
args.get("party"), args.get("party_account"), total_field)
|
||||
args.get("party"), args.get("party_account"), party_account_currency, company_currency)
|
||||
|
||||
# Get positive outstanding sales /purchase invoices/ Fees
|
||||
outstanding_invoices = get_outstanding_invoices(args.get("party_type"), args.get("party"),
|
||||
@ -580,28 +578,34 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre
|
||||
|
||||
return order_list
|
||||
|
||||
def get_negative_outstanding_invoices(party_type, party, party_account, total_field):
|
||||
if party_type != "Employee":
|
||||
voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"
|
||||
return frappe.db.sql("""
|
||||
select
|
||||
"{voucher_type}" as voucher_type, name as voucher_no,
|
||||
{total_field} as invoice_amount, outstanding_amount, posting_date,
|
||||
due_date, conversion_rate as exchange_rate
|
||||
from
|
||||
`tab{voucher_type}`
|
||||
where
|
||||
{party_type} = %s and {party_account} = %s and docstatus = 1 and outstanding_amount < 0
|
||||
order by
|
||||
posting_date, name
|
||||
""".format(**{
|
||||
"total_field": total_field,
|
||||
"voucher_type": voucher_type,
|
||||
"party_type": scrub(party_type),
|
||||
"party_account": "debit_to" if party_type=="Customer" else "credit_to"
|
||||
}), (party, party_account), as_dict = True)
|
||||
def get_negative_outstanding_invoices(party_type, party, party_account, party_account_currency, company_currency):
|
||||
voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"
|
||||
if party_account_currency == company_currency:
|
||||
grand_total_field = "base_grand_total"
|
||||
rounded_total_field = "base_rounded_total"
|
||||
else:
|
||||
return []
|
||||
grand_total_field = "grand_total"
|
||||
rounded_total_field = "rounded_total"
|
||||
|
||||
return frappe.db.sql("""
|
||||
select
|
||||
"{voucher_type}" as voucher_type, name as voucher_no,
|
||||
if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount,
|
||||
outstanding_amount, posting_date,
|
||||
due_date, conversion_rate as exchange_rate
|
||||
from
|
||||
`tab{voucher_type}`
|
||||
where
|
||||
{party_type} = %s and {party_account} = %s and docstatus = 1 and outstanding_amount < 0
|
||||
order by
|
||||
posting_date, name
|
||||
""".format(**{
|
||||
"rounded_total_field": rounded_total_field,
|
||||
"grand_total_field": grand_total_field,
|
||||
"voucher_type": voucher_type,
|
||||
"party_type": scrub(party_type),
|
||||
"party_account": "debit_to" if party_type == "Customer" else "credit_to"
|
||||
}), (party, party_account), as_dict=True)
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_party_details(company, party_type, party, date):
|
||||
@ -721,7 +725,10 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
||||
if party_amount:
|
||||
grand_total = outstanding_amount = party_amount
|
||||
elif dt in ("Sales Invoice", "Purchase Invoice"):
|
||||
grand_total = doc.base_grand_total if party_account_currency == doc.company_currency else doc.grand_total
|
||||
if party_account_currency == doc.company_currency:
|
||||
grand_total = doc.base_rounded_total or doc.base_grand_total
|
||||
else:
|
||||
grand_total = doc.rounded_total or doc.grand_total
|
||||
outstanding_amount = doc.outstanding_amount
|
||||
elif dt in ("Expense Claim"):
|
||||
grand_total = doc.total_sanctioned_amount
|
||||
@ -730,8 +737,10 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
||||
grand_total = doc.grand_total
|
||||
outstanding_amount = doc.outstanding_amount
|
||||
else:
|
||||
total_field = "base_grand_total" if party_account_currency == doc.company_currency else "grand_total"
|
||||
grand_total = flt(doc.get(total_field))
|
||||
if party_account_currency == doc.company_currency:
|
||||
grand_total = flt(doc.get("base_rounded_total") or doc.base_grand_total)
|
||||
else:
|
||||
grand_total = flt(doc.get("rounded_total") or doc.grand_total)
|
||||
outstanding_amount = grand_total - flt(doc.advance_paid)
|
||||
|
||||
# bank or cash
|
||||
@ -766,20 +775,51 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
||||
pe.received_amount = received_amount
|
||||
pe.allocate_payment_amount = 1
|
||||
pe.letter_head = doc.get("letter_head")
|
||||
args = {
|
||||
'party_account': party_account, 'company': pe.company, 'party_type': pe.party_type,
|
||||
'party': pe.party, 'posting_date': pe.posting_date
|
||||
}
|
||||
references = get_outstanding_reference_documents(args=args)
|
||||
|
||||
pe.append("references", {
|
||||
"reference_doctype": dt,
|
||||
"reference_name": dn,
|
||||
"bill_no": doc.get("bill_no"),
|
||||
"due_date": doc.get("due_date"),
|
||||
"total_amount": grand_total,
|
||||
"outstanding_amount": outstanding_amount,
|
||||
"allocated_amount": outstanding_amount
|
||||
})
|
||||
for reference in references:
|
||||
if reference.voucher_no == dn:
|
||||
allocated_amount = min(paid_amount, reference.outstanding_amount)
|
||||
pe.append("references", {
|
||||
'reference_doctype': reference.voucher_type,
|
||||
'reference_name': reference.voucher_no,
|
||||
'due_date': reference.due_date,
|
||||
'total_amount': reference.invoice_amount,
|
||||
'outstanding_amount': reference.outstanding_amount,
|
||||
'allocated_amount': allocated_amount,
|
||||
"bill_no": reference.get("bill_no")
|
||||
})
|
||||
if paid_amount:
|
||||
paid_amount -= allocated_amount
|
||||
|
||||
pe.setup_party_account_field()
|
||||
pe.set_missing_values()
|
||||
if party_account and bank:
|
||||
pe.set_exchange_rate()
|
||||
pe.set_amounts()
|
||||
return pe
|
||||
return pe
|
||||
|
||||
|
||||
def get_paid_amount(dt, dn, party_type, party, account, due_date):
|
||||
if party_type=="Customer":
|
||||
dr_or_cr = "credit_in_account_currency - debit_in_account_currency"
|
||||
else:
|
||||
dr_or_cr = "debit_in_account_currency - credit_in_account_currency"
|
||||
|
||||
paid_amount = frappe.db.sql("""
|
||||
select ifnull(sum({dr_or_cr}), 0) as paid_amount
|
||||
from `tabGL Entry`
|
||||
where against_voucher_type = %s
|
||||
and against_voucher = %s
|
||||
and party_type = %s
|
||||
and party = %s
|
||||
and account = %s
|
||||
and due_date = %s
|
||||
and {dr_or_cr} > 0
|
||||
""".format(dr_or_cr=dr_or_cr), (dt, dn, party_type, party, account, due_date))
|
||||
|
||||
return paid_amount[0][0] if paid_amount else 0
|
@ -14,6 +14,7 @@ from erpnext.hr.doctype.expense_claim.test_expense_claim import make_expense_cla
|
||||
|
||||
test_dependencies = ["Item"]
|
||||
|
||||
|
||||
class TestPaymentEntry(unittest.TestCase):
|
||||
def test_payment_entry_against_order(self):
|
||||
so = make_sales_order()
|
||||
@ -40,7 +41,7 @@ class TestPaymentEntry(unittest.TestCase):
|
||||
self.assertEqual(so_advance_paid, 0)
|
||||
|
||||
def test_payment_entry_against_si_usd_to_usd(self):
|
||||
si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
|
||||
si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
|
||||
currency="USD", conversion_rate=50)
|
||||
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC")
|
||||
pe.reference_no = "1"
|
||||
@ -65,8 +66,20 @@ class TestPaymentEntry(unittest.TestCase):
|
||||
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
|
||||
self.assertEqual(outstanding_amount, 100)
|
||||
|
||||
def test_payment_entry_against_si_multi_due_dates(self):
|
||||
si = create_sales_invoice(do_not_save=1)
|
||||
si.payment_terms_template = '_Test Payment Term Template'
|
||||
si.insert()
|
||||
si.submit()
|
||||
|
||||
pe = get_payment_entry(si.doctype, si.name)
|
||||
pe.reference_no = "1"
|
||||
pe.reference_date = "2016-01-01"
|
||||
pe.insert()
|
||||
pe.submit()
|
||||
|
||||
def test_payment_entry_against_pi(self):
|
||||
pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC",
|
||||
pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC",
|
||||
currency="USD", conversion_rate=50)
|
||||
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank USD - _TC")
|
||||
pe.reference_no = "1"
|
||||
@ -88,7 +101,7 @@ class TestPaymentEntry(unittest.TestCase):
|
||||
def test_payment_entry_against_ec(self):
|
||||
|
||||
payable = frappe.db.get_value('Company', "_Test Company", 'default_payable_account')
|
||||
ec = make_expense_claim(payable, 300, 300, "_Test Company","Travel Expenses - _TC")
|
||||
ec = make_expense_claim(payable, 300, 300, "_Test Company", "Travel Expenses - _TC")
|
||||
pe = get_payment_entry("Expense Claim", ec.name, bank_account="_Test Bank USD - _TC", bank_amount=300)
|
||||
pe.reference_no = "1"
|
||||
pe.reference_date = "2016-01-01"
|
||||
@ -108,7 +121,7 @@ class TestPaymentEntry(unittest.TestCase):
|
||||
self.assertEqual(outstanding_amount, 0)
|
||||
|
||||
def test_payment_entry_against_si_usd_to_inr(self):
|
||||
si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
|
||||
si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
|
||||
currency="USD", conversion_rate=50)
|
||||
pe = get_payment_entry("Sales Invoice", si.name, party_amount=20,
|
||||
bank_account="_Test Bank - _TC", bank_amount=900)
|
||||
@ -212,7 +225,7 @@ class TestPaymentEntry(unittest.TestCase):
|
||||
|
||||
self.assertRaises(InvalidPaymentEntry, pe1.validate)
|
||||
|
||||
si1 = create_sales_invoice()
|
||||
si1 = create_sales_invoice()
|
||||
|
||||
# create full payment entry against si1
|
||||
pe2 = get_payment_entry("Sales Invoice", si1.name, bank_account="_Test Cash - _TC")
|
||||
|
197
erpnext/accounts/doctype/payment_schedule/payment_schedule.json
Normal file
197
erpnext/accounts/doctype/payment_schedule/payment_schedule.json
Normal file
@ -0,0 +1,197 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "",
|
||||
"beta": 0,
|
||||
"creation": "2017-08-10 15:38:00.080575",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fieldname": "payment_term",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Term",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Payment Term",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Description",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "payment_term.description",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fieldname": "due_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Due Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fieldname": "invoice_portion",
|
||||
"fieldtype": "Percent",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Invoice Portion",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "payment_term.invoice_portion",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fieldname": "payment_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Amount",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "currency",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-11-21 19:23:08.490659",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Schedule",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class PaymentSchedule(Document):
|
||||
pass
|
0
erpnext/accounts/doctype/payment_term/__init__.py
Normal file
0
erpnext/accounts/doctype/payment_term/__init__.py
Normal file
2
erpnext/accounts/doctype/payment_term/payment_term.js
Normal file
2
erpnext/accounts/doctype/payment_term/payment_term.js
Normal file
@ -0,0 +1,2 @@
|
||||
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
344
erpnext/accounts/doctype/payment_term/payment_term.json
Normal file
344
erpnext/accounts/doctype/payment_term/payment_term.json
Normal file
@ -0,0 +1,344 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "field:payment_term_name",
|
||||
"beta": 0,
|
||||
"creation": "2017-08-10 15:24:54.876365",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 1,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "payment_term_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Term Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 1,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "invoice_portion",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Invoice Portion",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 1,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "due_date_based_on",
|
||||
"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": "Due Date Based On",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Day(s) after invoice date\nDay(s) after the end of the invoice month\nMonth(s) after the end of the invoice month",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 1,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:in_list(['Day(s) after invoice date', 'Day(s) after the end of the invoice month'], doc.due_date_based_on)",
|
||||
"fieldname": "credit_days",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Credit Days",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.due_date_based_on=='Month(s) after the end of the invoice month'",
|
||||
"fieldname": "credit_months",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Credit Months",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_6",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 1,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Small Text",
|
||||
"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": "Description",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-08-10 16:26:03.581501",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Term",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
}
|
10
erpnext/accounts/doctype/payment_term/payment_term.py
Normal file
10
erpnext/accounts/doctype/payment_term/payment_term.py
Normal file
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class PaymentTerm(Document):
|
||||
pass
|
23
erpnext/accounts/doctype/payment_term/test_payment_term.js
Normal file
23
erpnext/accounts/doctype/payment_term/test_payment_term.js
Normal file
@ -0,0 +1,23 @@
|
||||
/* eslint-disable */
|
||||
// rename this file from _test_[name] to test_[name] to activate
|
||||
// and remove above this line
|
||||
|
||||
QUnit.test("test: Payment Term", function (assert) {
|
||||
let done = assert.async();
|
||||
|
||||
// number of asserts
|
||||
assert.expect(1);
|
||||
|
||||
frappe.run_serially([
|
||||
// insert a new Payment Term
|
||||
() => frappe.tests.make('Payment Term', [
|
||||
// values to be set
|
||||
{key: 'value'}
|
||||
]),
|
||||
() => {
|
||||
assert.equal(cur_frm.doc.key, 'value');
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
|
||||
});
|
@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
import unittest
|
||||
|
||||
|
||||
class TestPaymentTerm(unittest.TestCase):
|
||||
pass
|
34
erpnext/accounts/doctype/payment_term/test_records.json
Normal file
34
erpnext/accounts/doctype/payment_term/test_records.json
Normal file
@ -0,0 +1,34 @@
|
||||
[
|
||||
{
|
||||
"doctype":"Payment Term",
|
||||
"due_date_based_on":"Day(s) after invoice date",
|
||||
"payment_term_name":"_Test N30",
|
||||
"description":"_Test Net 30 Days",
|
||||
"invoice_portion":50,
|
||||
"credit_days":30
|
||||
},
|
||||
{
|
||||
"doctype":"Payment Term",
|
||||
"due_date_based_on":"Day(s) after invoice date",
|
||||
"payment_term_name":"_Test COD",
|
||||
"description":"_Test Cash on Delivery",
|
||||
"invoice_portion":50,
|
||||
"credit_days":0
|
||||
},
|
||||
{
|
||||
"doctype":"Payment Term",
|
||||
"due_date_based_on":"Month(s) after the end of the invoice month",
|
||||
"payment_term_name":"_Test EONM",
|
||||
"description":"_Test End of Next Month",
|
||||
"invoice_portion":100,
|
||||
"credit_months":1
|
||||
},
|
||||
{
|
||||
"doctype":"Payment Term",
|
||||
"due_date_based_on":"Day(s) after invoice date",
|
||||
"payment_term_name":"_Test N30 1",
|
||||
"description":"_Test Net 30 Days",
|
||||
"invoice_portion":100,
|
||||
"credit_days":30
|
||||
}
|
||||
]
|
@ -0,0 +1,12 @@
|
||||
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Payment Terms Template', {
|
||||
setup: function(frm) {
|
||||
frm.add_fetch("payment_term", "description", "description");
|
||||
frm.add_fetch("payment_term", "invoice_portion", "invoice_portion");
|
||||
frm.add_fetch("payment_term", "due_date_based_on", "due_date_based_on");
|
||||
frm.add_fetch("payment_term", "credit_days", "credit_days");
|
||||
frm.add_fetch("payment_term", "credit_months", "credit_months");
|
||||
}
|
||||
});
|
@ -0,0 +1,164 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "field:template_name",
|
||||
"beta": 0,
|
||||
"creation": "2017-08-10 15:34:28.058054",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "template_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Template Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "terms",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Terms",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Payment Terms Template Detail",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-08-10 15:46:33.877884",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Terms Template",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import flt, cint
|
||||
from frappe import _
|
||||
|
||||
|
||||
class PaymentTermsTemplate(Document):
|
||||
def validate(self):
|
||||
self.validate_invoice_portion()
|
||||
self.validate_credit_days()
|
||||
self.check_duplicate_terms()
|
||||
|
||||
def validate_invoice_portion(self):
|
||||
total_portion = 0
|
||||
for term in self.terms:
|
||||
total_portion += flt(term.get('invoice_portion', 0))
|
||||
|
||||
if flt(total_portion, 2) != 100.00:
|
||||
frappe.msgprint(_('Combined invoice portion must equal 100%'), raise_exception=1, indicator='red')
|
||||
|
||||
def validate_credit_days(self):
|
||||
for term in self.terms:
|
||||
if cint(term.credit_days) < 0:
|
||||
frappe.msgprint(_('Credit Days cannot be a negative number'), raise_exception=1, indicator='red')
|
||||
|
||||
def check_duplicate_terms(self):
|
||||
terms = []
|
||||
for term in self.terms:
|
||||
term_info = (term.credit_days, term.due_date_based_on)
|
||||
if term_info in terms:
|
||||
frappe.msgprint(
|
||||
_('The Payment Term at row {0} is possibly a duplicate.').format(term.idx),
|
||||
raise_exception=1, indicator='red'
|
||||
)
|
||||
else:
|
||||
terms.append(term_info)
|
@ -0,0 +1,23 @@
|
||||
/* eslint-disable */
|
||||
// rename this file from _test_[name] to test_[name] to activate
|
||||
// and remove above this line
|
||||
|
||||
QUnit.test("test: Payment Terms Template", function (assert) {
|
||||
let done = assert.async();
|
||||
|
||||
// number of asserts
|
||||
assert.expect(1);
|
||||
|
||||
frappe.run_serially([
|
||||
// insert a new Payment Terms Template
|
||||
() => frappe.tests.make('Payment Terms Template', [
|
||||
// values to be set
|
||||
{key: 'value'}
|
||||
]),
|
||||
() => {
|
||||
assert.equal(cur_frm.doc.key, 'value');
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
|
||||
});
|
@ -0,0 +1,72 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
|
||||
|
||||
class TestPaymentTermsTemplate(unittest.TestCase):
|
||||
def tearDown(self):
|
||||
frappe.delete_doc('Payment Terms Template', '_Test Payment Terms Template For Test', force=1)
|
||||
|
||||
def test_create_template(self):
|
||||
template = frappe.get_doc({
|
||||
'doctype': 'Payment Terms Template',
|
||||
'template_name': '_Test Payment Terms Template For Test',
|
||||
'terms': [{
|
||||
'doctype': 'Payment Terms Template Detail',
|
||||
'invoice_portion': 50.00,
|
||||
'credit_days_based_on': 'Day(s) after invoice date',
|
||||
'credit_days': 30
|
||||
}]
|
||||
})
|
||||
|
||||
self.assertRaises(frappe.ValidationError, template.insert)
|
||||
|
||||
template.append('terms', {
|
||||
'doctype': 'Payment Terms Template Detail',
|
||||
'invoice_portion': 50.00,
|
||||
'credit_days_based_on': 'Day(s) after invoice date',
|
||||
'credit_days': 0
|
||||
})
|
||||
|
||||
template.insert()
|
||||
|
||||
def test_credit_days(self):
|
||||
template = frappe.get_doc({
|
||||
'doctype': 'Payment Terms Template',
|
||||
'template_name': '_Test Payment Terms Template For Test',
|
||||
'terms': [{
|
||||
'doctype': 'Payment Terms Template Detail',
|
||||
'invoice_portion': 100.00,
|
||||
'credit_days_based_on': 'Day(s) after invoice date',
|
||||
'credit_days': -30
|
||||
}]
|
||||
})
|
||||
|
||||
self.assertRaises(frappe.ValidationError, template.insert)
|
||||
|
||||
def test_duplicate_terms(self):
|
||||
template = frappe.get_doc({
|
||||
'doctype': 'Payment Terms Template',
|
||||
'template_name': '_Test Payment Terms Template For Test',
|
||||
'terms': [
|
||||
{
|
||||
'doctype': 'Payment Terms Template Detail',
|
||||
'invoice_portion': 50.00,
|
||||
'credit_days_based_on': 'Day(s) after invoice date',
|
||||
'credit_days': 30
|
||||
},
|
||||
{
|
||||
'doctype': 'Payment Terms Template Detail',
|
||||
'invoice_portion': 50.00,
|
||||
'credit_days_based_on': 'Day(s) after invoice date',
|
||||
'credit_days': 30
|
||||
}
|
||||
|
||||
]
|
||||
})
|
||||
|
||||
self.assertRaises(frappe.ValidationError, template.insert)
|
@ -0,0 +1,60 @@
|
||||
[
|
||||
{
|
||||
"doctype":"Payment Terms Template",
|
||||
"terms":[
|
||||
{
|
||||
"doctype":"Payment Terms Template Detail",
|
||||
"due_date_based_on":"Day(s) after invoice date",
|
||||
"idx":1,
|
||||
"description":"Cash on Delivery",
|
||||
"invoice_portion":50,
|
||||
"credit_days":0,
|
||||
"credit_months":0,
|
||||
"payment_term":"_Test COD"
|
||||
},
|
||||
{
|
||||
"doctype":"Payment Terms Template Detail",
|
||||
"due_date_based_on":"Day(s) after invoice date",
|
||||
"idx":2,
|
||||
"description":"Net 30 Days ",
|
||||
"invoice_portion":50,
|
||||
"credit_days":30,
|
||||
"credit_months":0,
|
||||
"payment_term":"_Test N30"
|
||||
}
|
||||
],
|
||||
"template_name":"_Test Payment Term Template"
|
||||
},
|
||||
{
|
||||
"doctype":"Payment Terms Template",
|
||||
"terms":[
|
||||
{
|
||||
"doctype":"Payment Terms Template Detail",
|
||||
"due_date_based_on":"Month(s) after the end of the invoice month",
|
||||
"idx":1,
|
||||
"description":"_Test End of Next Months",
|
||||
"invoice_portion":100,
|
||||
"credit_days":0,
|
||||
"credit_months":1,
|
||||
"payment_term":"_Test EONM"
|
||||
}
|
||||
],
|
||||
"template_name":"_Test Payment Term Template 1"
|
||||
},
|
||||
{
|
||||
"doctype":"Payment Terms Template",
|
||||
"terms":[
|
||||
{
|
||||
"doctype":"Payment Terms Template Detail",
|
||||
"due_date_based_on":"Day(s) after invoice date",
|
||||
"idx":1,
|
||||
"description":"_Test Net Within 30 days",
|
||||
"invoice_portion":100,
|
||||
"credit_days":30,
|
||||
"credit_months":0,
|
||||
"payment_term":"_Test N30 1"
|
||||
}
|
||||
],
|
||||
"template_name":"_Test Payment Term Template 3"
|
||||
}
|
||||
]
|
@ -0,0 +1,232 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "PTTD.#####",
|
||||
"beta": 0,
|
||||
"creation": "2017-08-10 15:34:09.409562",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fieldname": "payment_term",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Term",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Payment Term",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Description",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"default": "0",
|
||||
"fieldname": "invoice_portion",
|
||||
"fieldtype": "Percent",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Invoice Portion",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fieldname": "due_date_based_on",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Due Date Based On",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Day(s) after invoice date\nDay(s) after the end of the invoice month\nMonth(s) after the end of the invoice month",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"default": "0",
|
||||
"depends_on": "eval:in_list(['Day(s) after invoice date', 'Day(s) after the end of the invoice month'], doc.due_date_based_on)",
|
||||
"fieldname": "credit_days",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Credit Days",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "0",
|
||||
"depends_on": "eval:doc.due_date_based_on=='Month(s) after the end of the invoice month'",
|
||||
"fieldname": "credit_months",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Credit Months",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-09-26 05:21:51.738319",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Terms Template Detail",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class PaymentTermsTemplateDetail(Document):
|
||||
pass
|
@ -252,9 +252,11 @@ class TestPricingRule(unittest.TestCase):
|
||||
self.assertEquals(so.items[0].rate, 100)
|
||||
|
||||
def test_pricing_rule_with_margin_and_discount(self):
|
||||
frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
|
||||
make_pricing_rule(selling=1, margin_type="Percentage", margin_rate_or_amount=10)
|
||||
si = create_sales_invoice(do_not_save=True)
|
||||
si.items[0].price_list_rate = 1000
|
||||
si.payment_schedule = []
|
||||
si.insert(ignore_permissions=True)
|
||||
|
||||
item = si.items[0]
|
||||
@ -263,6 +265,7 @@ class TestPricingRule(unittest.TestCase):
|
||||
|
||||
# With discount
|
||||
item.discount_percentage = 10
|
||||
si.payment_schedule = []
|
||||
si.save()
|
||||
item = si.items[0]
|
||||
self.assertEquals(item.rate, 990)
|
||||
|
@ -378,5 +378,5 @@ frappe.ui.form.on("Purchase Invoice", {
|
||||
erpnext.buying.get_default_bom(frm);
|
||||
}
|
||||
frm.toggle_reqd("supplier_warehouse", frm.doc.is_subcontracted==="Yes");
|
||||
},
|
||||
}
|
||||
})
|
@ -2973,6 +2973,99 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "eval:(!doc.is_return)",
|
||||
"columns": 0,
|
||||
"fieldname": "payment_schedule_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Terms",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "payment_terms_template",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Terms Template",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Payment Terms Template",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "payment_schedule",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Schedule",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Payment Schedule",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@ -3760,7 +3853,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2017-11-16 01:04:15.308603",
|
||||
"modified": "2017-11-21 01:04:15.308603",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice",
|
||||
|
@ -95,7 +95,7 @@ class PurchaseInvoice(BuyingController):
|
||||
if not self.credit_to:
|
||||
self.credit_to = get_party_account("Supplier", self.supplier, self.company)
|
||||
if not self.due_date:
|
||||
self.due_date = get_due_date(self.posting_date, "Supplier", self.supplier, self.company)
|
||||
self.due_date = get_due_date(self.posting_date, "Supplier", self.supplier)
|
||||
|
||||
super(PurchaseInvoice, self).set_missing_values(for_validate)
|
||||
|
||||
@ -362,7 +362,27 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
def make_supplier_gl_entry(self, gl_entries):
|
||||
grand_total = self.rounded_total or self.grand_total
|
||||
if grand_total:
|
||||
if self.get("payment_schedule"):
|
||||
for d in self.get("payment_schedule"):
|
||||
payment_amount_in_company_currency = flt(d.payment_amount * self.conversion_rate,
|
||||
d.precision("payment_amount"))
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": self.credit_to,
|
||||
"party_type": "Supplier",
|
||||
"party": self.supplier,
|
||||
"due_date": d.due_date,
|
||||
"against": self.against_expense_account,
|
||||
"credit": payment_amount_in_company_currency,
|
||||
"credit_in_account_currency": payment_amount_in_company_currency \
|
||||
if self.party_account_currency==self.company_currency else d.payment_amount,
|
||||
"against_voucher": self.return_against if cint(self.is_return) else self.name,
|
||||
"against_voucher_type": self.doctype
|
||||
}, self.party_account_currency)
|
||||
)
|
||||
|
||||
elif grand_total:
|
||||
# Didnot use base_grand_total to book rounding loss gle
|
||||
grand_total_in_company_currency = flt(grand_total * self.conversion_rate,
|
||||
self.precision("grand_total"))
|
||||
|
@ -20,7 +20,8 @@ QUnit.test("test purchase invoice", function(assert) {
|
||||
{contact_person: 'Contact 3-Test Supplier'},
|
||||
{taxes_and_charges: 'TEST In State GST'},
|
||||
{tc_name: 'Test Term 1'},
|
||||
{terms: 'This is Test'}
|
||||
{terms: 'This is Test'},
|
||||
{payment_terms_template: '_Test Payment Term Template UI'}
|
||||
]);
|
||||
},
|
||||
() => cur_frm.save(),
|
||||
@ -34,6 +35,9 @@ QUnit.test("test purchase invoice", function(assert) {
|
||||
// grand_total Calculated
|
||||
assert.ok(cur_frm.doc.grand_total==590, "Grad Total correct");
|
||||
|
||||
assert.ok(cur_frm.doc.payment_terms_template, "Payment Terms Template is correct");
|
||||
assert.ok(cur_frm.doc.payment_schedule.length > 0, "Payment Term Schedule is not empty");
|
||||
|
||||
},
|
||||
() => frappe.tests.click_button('Submit'),
|
||||
() => frappe.tests.click_button('Yes'),
|
||||
|
@ -6,15 +6,16 @@ from __future__ import unicode_literals
|
||||
import unittest
|
||||
import frappe, erpnext
|
||||
import frappe.model
|
||||
from frappe.utils import cint, flt, today, nowdate
|
||||
from frappe.utils import cint, flt, today, nowdate, getdate, add_days
|
||||
import frappe.defaults
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \
|
||||
test_records as pr_test_records
|
||||
from erpnext.controllers.accounts_controller import get_payment_terms
|
||||
from erpnext.exceptions import InvalidCurrency
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_transaction
|
||||
from erpnext.accounts.doctype.account.test_account import get_inventory_account
|
||||
|
||||
test_dependencies = ["Item", "Cost Center"]
|
||||
test_dependencies = ["Item", "Cost Center", "Payment Term", "Payment Terms Template"]
|
||||
test_ignore = ["Serial No"]
|
||||
|
||||
class TestPurchaseInvoice(unittest.TestCase):
|
||||
@ -62,6 +63,12 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
|
||||
set_perpetual_inventory(0, pi.company)
|
||||
|
||||
def test_terms_added_after_save(self):
|
||||
pi = frappe.copy_doc(test_records[1])
|
||||
pi.insert()
|
||||
self.assertTrue(pi.payment_schedule)
|
||||
self.assertEqual(pi.payment_schedule[0].due_date, pi.due_date)
|
||||
|
||||
def test_payment_entry_unlink_against_purchase_invoice(self):
|
||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
|
||||
unlink_payment_on_cancel_of_invoice(0)
|
||||
@ -248,6 +255,7 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
self.assertEqual(pi.outstanding_amount, 1212.30)
|
||||
|
||||
pi.disable_rounded_total = 0
|
||||
pi.get("payment_schedule")[0].payment_amount = 1512.0
|
||||
pi.save()
|
||||
self.assertEqual(pi.outstanding_amount, 1212.0)
|
||||
|
||||
@ -263,6 +271,56 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
self.assertFalse(frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||
where reference_type='Purchase Invoice' and reference_name=%s""", pi.name))
|
||||
|
||||
def test_invoice_with_advance_and_multi_payment_terms(self):
|
||||
from erpnext.accounts.doctype.journal_entry.test_journal_entry \
|
||||
import test_records as jv_test_records
|
||||
|
||||
jv = frappe.copy_doc(jv_test_records[1])
|
||||
jv.insert()
|
||||
jv.submit()
|
||||
|
||||
pi = frappe.copy_doc(test_records[0])
|
||||
pi.disable_rounded_total = 1
|
||||
pi.append("advances", {
|
||||
"reference_type": "Journal Entry",
|
||||
"reference_name": jv.name,
|
||||
"reference_row": jv.get("accounts")[0].name,
|
||||
"advance_amount": 400,
|
||||
"allocated_amount": 300,
|
||||
"remarks": jv.remark
|
||||
})
|
||||
pi.insert()
|
||||
|
||||
pi.update({
|
||||
"payment_schedule": get_payment_terms("_Test Payment Term Template",
|
||||
pi.posting_date, pi.grand_total)
|
||||
})
|
||||
|
||||
pi.save()
|
||||
pi.submit()
|
||||
self.assertEqual(pi.payment_schedule[0].payment_amount, 756.15)
|
||||
self.assertEqual(pi.payment_schedule[0].due_date, pi.posting_date)
|
||||
self.assertEqual(pi.payment_schedule[1].payment_amount, 756.15)
|
||||
self.assertEqual(pi.payment_schedule[1].due_date, add_days(pi.posting_date, 30))
|
||||
|
||||
pi.load_from_db()
|
||||
|
||||
self.assertTrue(
|
||||
frappe.db.sql(
|
||||
"select name from `tabJournal Entry Account` where reference_type='Purchase Invoice' and "
|
||||
"reference_name=%s and debit_in_account_currency=300", pi.name)
|
||||
)
|
||||
|
||||
self.assertEqual(pi.outstanding_amount, 1212.30)
|
||||
|
||||
pi.cancel()
|
||||
|
||||
self.assertFalse(
|
||||
frappe.db.sql(
|
||||
"select name from `tabJournal Entry Account` where reference_type='Purchase Invoice' and "
|
||||
"reference_name=%s", pi.name)
|
||||
)
|
||||
|
||||
def test_total_purchase_cost_for_project(self):
|
||||
existing_purchase_cost = frappe.db.sql("""select sum(base_net_amount)
|
||||
from `tabPurchase Invoice Item` where project = '_Test Project' and docstatus=1""")
|
||||
@ -589,6 +647,55 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
self.assertEquals(pi.total_taxes_and_charges, 462.3)
|
||||
self.assertEquals(pi.grand_total, 1712.3)
|
||||
|
||||
def test_gl_entry_based_on_payment_schedule(self):
|
||||
pi = make_purchase_invoice(do_not_save=True, supplier="_Test Supplier P")
|
||||
pi.append("payment_schedule", {
|
||||
"due_date": add_days(nowdate(), 15),
|
||||
"payment_amount": 100,
|
||||
"invoice_portion": 40.00
|
||||
})
|
||||
pi.append("payment_schedule", {
|
||||
"due_date": add_days(nowdate(), 25),
|
||||
"payment_amount": 150,
|
||||
"invoice_portion": 60.00
|
||||
})
|
||||
|
||||
pi.save()
|
||||
pi.submit()
|
||||
|
||||
gl_entries = frappe.db.sql("""select account, debit, credit, due_date
|
||||
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
|
||||
order by account asc, debit asc""", pi.name, as_dict=1)
|
||||
self.assertTrue(gl_entries)
|
||||
|
||||
expected_gl_entries = sorted([
|
||||
[pi.credit_to, 0.0, 100.0, add_days(nowdate(), 15)],
|
||||
[pi.credit_to, 0.0, 150.0, add_days(nowdate(), 25)],
|
||||
["_Test Account Cost for Goods Sold - _TC", 250.0, 0.0, None]
|
||||
])
|
||||
|
||||
for i, gle in enumerate(sorted(gl_entries, key=lambda gle: gle.account)):
|
||||
self.assertEquals(expected_gl_entries[i][0], gle.account)
|
||||
self.assertEquals(expected_gl_entries[i][1], gle.debit)
|
||||
self.assertEquals(expected_gl_entries[i][2], gle.credit)
|
||||
self.assertEquals(getdate(expected_gl_entries[i][3]), getdate(gle.due_date))
|
||||
|
||||
def test_make_pi_without_terms(self):
|
||||
pi = make_purchase_invoice(do_not_save=1)
|
||||
|
||||
self.assertFalse(pi.get('payment_schedule'))
|
||||
|
||||
pi.insert()
|
||||
|
||||
self.assertTrue(pi.get('payment_schedule'))
|
||||
|
||||
def test_duplicate_due_date_in_terms(self):
|
||||
pi = make_purchase_invoice(do_not_save=1)
|
||||
pi.append('payment_schedule', dict(due_date='2017-01-01', invoice_portion=50.00, payment_amount=50))
|
||||
pi.append('payment_schedule', dict(due_date='2017-01-01', invoice_portion=50.00, payment_amount=50))
|
||||
|
||||
self.assertRaises(frappe.ValidationError, pi.insert)
|
||||
|
||||
def unlink_payment_on_cancel_of_invoice(enable=1):
|
||||
accounts_settings = frappe.get_doc("Accounts Settings")
|
||||
accounts_settings.unlink_payment_on_cancellation_of_invoice = enable
|
||||
|
@ -607,3 +607,4 @@ var calculate_total_billing_amount = function(frm) {
|
||||
|
||||
refresh_field('total_billing_amount')
|
||||
}
|
||||
|
||||
|
@ -2748,6 +2748,101 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "eval:(!doc.is_pos && !doc.is_return)",
|
||||
"columns": 0,
|
||||
"fieldname": "payment_schedule_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Terms",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:(!doc.is_pos && !doc.is_return)",
|
||||
"fieldname": "payment_terms_template",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Terms Template",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Payment Terms Template",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:(!doc.is_pos && !doc.is_return)",
|
||||
"fieldname": "payment_schedule",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Schedule",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Payment Schedule",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@ -3002,6 +3097,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "is_pos",
|
||||
"fieldname": "base_change_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@ -3062,6 +3158,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "is_pos",
|
||||
"fieldname": "change_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@ -3093,6 +3190,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "is_pos",
|
||||
"fieldname": "account_for_change_amount",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@ -4434,9 +4532,9 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2017-11-15 01:02:36.885752",
|
||||
"modified": "2017-11-17 01:02:36.885752",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
"name_case": "Title Case",
|
||||
"owner": "Administrator",
|
||||
|
@ -242,7 +242,7 @@ class SalesInvoice(SellingController):
|
||||
if not self.debit_to:
|
||||
self.debit_to = get_party_account("Customer", self.customer, self.company)
|
||||
if not self.due_date and self.customer:
|
||||
self.due_date = get_due_date(self.posting_date, "Customer", self.customer, self.company)
|
||||
self.due_date = get_due_date(self.posting_date, "Customer", self.customer)
|
||||
|
||||
super(SalesInvoice, self).set_missing_values(for_validate)
|
||||
|
||||
@ -633,7 +633,27 @@ class SalesInvoice(SellingController):
|
||||
|
||||
def make_customer_gl_entry(self, gl_entries):
|
||||
grand_total = self.rounded_total or self.grand_total
|
||||
if grand_total:
|
||||
if self.get("payment_schedule"):
|
||||
for d in self.get("payment_schedule"):
|
||||
payment_amount_in_company_currency = flt(d.payment_amount * self.conversion_rate,
|
||||
d.precision("payment_amount"))
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": self.debit_to,
|
||||
"party_type": "Customer",
|
||||
"party": self.customer,
|
||||
"due_date": d.due_date,
|
||||
"against": self.against_income_account,
|
||||
"debit": payment_amount_in_company_currency,
|
||||
"debit_in_account_currency": payment_amount_in_company_currency \
|
||||
if self.party_account_currency==self.company_currency else d.payment_amount,
|
||||
"against_voucher": self.return_against if cint(self.is_return) else self.name,
|
||||
"against_voucher_type": self.doctype
|
||||
}, self.party_account_currency)
|
||||
)
|
||||
|
||||
elif grand_total:
|
||||
# Didnot use base_grand_total to book rounding loss gle
|
||||
grand_total_in_company_currency = flt(grand_total * self.conversion_rate,
|
||||
self.precision("grand_total"))
|
||||
|
@ -1,7 +1,7 @@
|
||||
QUnit.module('Sales Invoice');
|
||||
|
||||
QUnit.test("test sales Invoice", function(assert) {
|
||||
assert.expect(4);
|
||||
assert.expect(6);
|
||||
let done = assert.async();
|
||||
frappe.run_serially([
|
||||
() => {
|
||||
@ -19,7 +19,8 @@ QUnit.test("test sales Invoice", function(assert) {
|
||||
{contact_person: 'Contact 1-Test Customer 1'},
|
||||
{taxes_and_charges: 'TEST In State GST'},
|
||||
{tc_name: 'Test Term 1'},
|
||||
{terms: 'This is Test'}
|
||||
{terms: 'This is Test'},
|
||||
{payment_terms_template: '_Test Payment Term Template UI'}
|
||||
]);
|
||||
},
|
||||
() => cur_frm.save(),
|
||||
@ -31,7 +32,10 @@ QUnit.test("test sales Invoice", function(assert) {
|
||||
// get tax account head details
|
||||
assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct");
|
||||
// grand_total Calculated
|
||||
assert.ok(cur_frm.doc.grand_total==590, "Grad Total correct");
|
||||
assert.ok(cur_frm.doc.grand_total==590, "Grand Total correct");
|
||||
|
||||
assert.ok(cur_frm.doc.payment_terms_template, "Payment Terms Template is correct");
|
||||
assert.ok(cur_frm.doc.payment_schedule.length > 0, "Payment Term Schedule is not empty");
|
||||
|
||||
},
|
||||
() => frappe.tests.click_button('Submit'),
|
||||
@ -40,4 +44,3 @@ QUnit.test("test sales Invoice", function(assert) {
|
||||
() => done()
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -3,8 +3,9 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
|
||||
import unittest, copy, time
|
||||
from frappe.utils import nowdate, add_days, flt, cint
|
||||
from frappe.utils import nowdate, add_days, flt, getdate, cint
|
||||
from frappe.model.dynamic_links import get_dynamic_link_map
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction
|
||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice
|
||||
@ -58,6 +59,13 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
self.assertRaises(frappe.CannotChangeConstantError, si.save)
|
||||
|
||||
def test_add_terms_after_save(self):
|
||||
si = frappe.copy_doc(test_records[2])
|
||||
si.insert()
|
||||
|
||||
self.assertTrue(si.payment_schedule)
|
||||
self.assertEqual(getdate(si.payment_schedule[0].due_date), getdate(si.due_date))
|
||||
|
||||
def test_sales_invoice_calculation_base_currency(self):
|
||||
si = frappe.copy_doc(test_records[2])
|
||||
si.insert()
|
||||
@ -199,6 +207,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
# additional discount
|
||||
si.discount_amount = 100
|
||||
si.apply_discount_on = 'Net Total'
|
||||
si.payment_schedule = []
|
||||
|
||||
si.save()
|
||||
|
||||
@ -211,6 +220,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
# additional discount on grand total
|
||||
si.discount_amount = 100
|
||||
si.apply_discount_on = 'Grand Total'
|
||||
si.payment_schedule = []
|
||||
|
||||
si.save()
|
||||
|
||||
@ -932,20 +942,6 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
self.assertEquals(si.get("items")[0].serial_no, dn.get("items")[0].serial_no)
|
||||
|
||||
def test_invoice_due_date_against_customers_credit_days(self):
|
||||
# set customer's credit days
|
||||
frappe.db.set_value("Customer", "_Test Customer", "credit_days_based_on", "Fixed Days")
|
||||
frappe.db.set_value("Customer", "_Test Customer", "credit_days", 10)
|
||||
|
||||
si = create_sales_invoice()
|
||||
self.assertEqual(si.due_date, add_days(nowdate(), 10))
|
||||
|
||||
# set customer's credit days is last day of the next month
|
||||
frappe.db.set_value("Customer", "_Test Customer", "credit_days_based_on", "Last Day of the Next Month")
|
||||
|
||||
si1 = create_sales_invoice(posting_date="2015-07-05")
|
||||
self.assertEqual(si1.due_date, "2015-08-31")
|
||||
|
||||
def test_return_sales_invoice(self):
|
||||
set_perpetual_inventory()
|
||||
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, basic_rate=100)
|
||||
@ -1325,6 +1321,40 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
})
|
||||
si.insert()
|
||||
return si
|
||||
|
||||
def test_gl_entry_based_on_payment_schedule(self):
|
||||
si = create_sales_invoice(do_not_save=True, customer="_Test Customer P")
|
||||
si.append("payment_schedule", {
|
||||
"due_date": add_days(nowdate(), 15),
|
||||
"payment_amount": 20,
|
||||
"invoice_portion": 20.00
|
||||
})
|
||||
si.append("payment_schedule", {
|
||||
"due_date": add_days(nowdate(), 45),
|
||||
"payment_amount": 80,
|
||||
"invoice_portion": 80.00
|
||||
})
|
||||
|
||||
si.save()
|
||||
si.submit()
|
||||
|
||||
gl_entries = frappe.db.sql("""select account, debit, credit, due_date
|
||||
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
|
||||
order by account asc, debit asc""", si.name, as_dict=1)
|
||||
self.assertTrue(gl_entries)
|
||||
|
||||
expected_gl_entries = sorted([
|
||||
[si.debit_to, 20.0, 0.0, add_days(nowdate(), 15)],
|
||||
[si.debit_to, 80.0, 0.0, add_days(nowdate(), 45)],
|
||||
["Sales - _TC", 0.0, 100.0, None]
|
||||
])
|
||||
|
||||
for i, gle in enumerate(sorted(gl_entries, key=lambda gle: gle.account)):
|
||||
self.assertEquals(expected_gl_entries[i][0], gle.account)
|
||||
self.assertEquals(expected_gl_entries[i][1], gle.debit)
|
||||
self.assertEquals(expected_gl_entries[i][2], gle.credit)
|
||||
self.assertEquals(getdate(expected_gl_entries[i][3]), getdate(gle.due_date))
|
||||
|
||||
|
||||
def test_company_monthly_sales(self):
|
||||
existing_current_month_sales = frappe.db.get_value("Company", "_Test Company", "total_monthly_sales")
|
||||
@ -1404,6 +1434,20 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
self.assertEquals(si.total_taxes_and_charges, 577.05)
|
||||
self.assertEquals(si.grand_total, 1827.05)
|
||||
|
||||
def test_create_invoice_without_terms(self):
|
||||
si = create_sales_invoice(do_not_save=1)
|
||||
self.assertFalse(si.get('payment_schedule'))
|
||||
|
||||
si.insert()
|
||||
self.assertTrue(si.get('payment_schedule'))
|
||||
|
||||
def test_duplicate_due_date_in_terms(self):
|
||||
si = create_sales_invoice(do_not_save=1)
|
||||
si.append('payment_schedule', dict(due_date='2017-01-01', invoice_portion=50.00, payment_amount=50))
|
||||
si.append('payment_schedule', dict(due_date='2017-01-01', invoice_portion=50.00, payment_amount=50))
|
||||
|
||||
self.assertRaises(frappe.ValidationError, si.insert)
|
||||
|
||||
def create_sales_invoice(**args):
|
||||
si = frappe.new_doc("Sales Invoice")
|
||||
args = frappe._dict(args)
|
||||
@ -1437,6 +1481,11 @@ def create_sales_invoice(**args):
|
||||
si.insert()
|
||||
if not args.do_not_submit:
|
||||
si.submit()
|
||||
else:
|
||||
si.payment_schedule = []
|
||||
else:
|
||||
si.payment_schedule = []
|
||||
|
||||
return si
|
||||
|
||||
test_dependencies = ["Journal Entry", "Contact", "Address"]
|
||||
|
@ -19,7 +19,8 @@ QUnit.test("test sales Invoice with payment", function(assert) {
|
||||
{contact_person: 'Contact 1-Test Customer 1'},
|
||||
{taxes_and_charges: 'TEST In State GST'},
|
||||
{tc_name: 'Test Term 1'},
|
||||
{terms: 'This is Test'}
|
||||
{terms: 'This is Test'},
|
||||
{payment_terms_template: '_Test Payment Term Template UI'}
|
||||
]);
|
||||
},
|
||||
() => cur_frm.save(),
|
||||
@ -43,6 +44,7 @@ QUnit.test("test sales Invoice with payment", function(assert) {
|
||||
() => { cur_frm.set_value('paid_to','Cash - '+frappe.get_abbr(frappe.defaults.get_default('Company')));},
|
||||
() => {cur_frm.set_value('reference_no','TEST1234');},
|
||||
() => {cur_frm.set_value('reference_date',frappe.datetime.add_days(frappe.datetime.nowdate(), 0));},
|
||||
() => cur_frm.set_value("payment_schedule", []),
|
||||
() => cur_frm.save(),
|
||||
() => {
|
||||
// get payment details
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe, erpnext
|
||||
from frappe.utils import flt, cstr, cint
|
||||
from frappe.utils import flt, cstr, cint, getdate
|
||||
from frappe import _
|
||||
from frappe.model.meta import get_field_precision
|
||||
from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
|
||||
@ -75,7 +75,8 @@ def check_if_in_list(gle, gl_map):
|
||||
and cstr(e.get('against_voucher'))==cstr(gle.get('against_voucher')) \
|
||||
and cstr(e.get('against_voucher_type')) == cstr(gle.get('against_voucher_type')) \
|
||||
and cstr(e.get('cost_center')) == cstr(gle.get('cost_center')) \
|
||||
and cstr(e.get('project')) == cstr(gle.get('project')):
|
||||
and cstr(e.get('project')) == cstr(gle.get('project')) \
|
||||
and getdate(e.get('due_date')) == getdate(gle.get('due_date')):
|
||||
return e
|
||||
|
||||
def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
|
||||
|
@ -9,7 +9,7 @@ from frappe import _, msgprint, scrub
|
||||
from frappe.defaults import get_user_permissions
|
||||
from frappe.model.utils import get_fetch_values
|
||||
from frappe.utils import (add_days, getdate, formatdate, get_first_day, date_diff,
|
||||
add_years, get_timestamp, nowdate, flt)
|
||||
add_years, get_timestamp, nowdate, flt, add_months, get_last_day)
|
||||
from frappe.contacts.doctype.address.address import (get_address_display,
|
||||
get_default_address, get_company_address)
|
||||
from frappe.contacts.doctype.contact.contact import get_contact_details, get_default_contact
|
||||
@ -51,6 +51,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)
|
||||
out["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company, out.customer_group, out.supplier_type)
|
||||
out["payment_terms_template"] = get_pyt_term_template(party.name, party_type)
|
||||
|
||||
if not out.get("currency"):
|
||||
out["currency"] = currency
|
||||
@ -163,7 +164,7 @@ def set_account_and_due_date(party, account, party_type, company, posting_date,
|
||||
out = {
|
||||
party_type.lower(): party,
|
||||
account_fieldname : account,
|
||||
"due_date": get_due_date(posting_date, party_type, party, company)
|
||||
"due_date": get_due_date(posting_date, party_type, party)
|
||||
}
|
||||
return out
|
||||
|
||||
@ -262,51 +263,54 @@ def validate_party_accounts(doc):
|
||||
|
||||
if doc.get("default_currency") and party_account_currency and company_default_currency:
|
||||
if doc.default_currency != party_account_currency and doc.default_currency != company_default_currency:
|
||||
frappe.throw(_("Billing currency must be equal to either default comapany's currency or party account currency"))
|
||||
frappe.throw(_("Billing currency must be equal to either default company's currency or party account currency"))
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_due_date(posting_date, party_type, party, company):
|
||||
"""Set Due Date = Posting Date + Credit Days"""
|
||||
def get_due_date(posting_date, party_type, party):
|
||||
"""Get due date from `Payment Terms Template`"""
|
||||
due_date = None
|
||||
if posting_date and party:
|
||||
due_date = posting_date
|
||||
credit_days_based_on, credit_days = get_credit_days(party_type, party, company)
|
||||
if credit_days_based_on == "Fixed Days" and credit_days:
|
||||
due_date = add_days(posting_date, credit_days)
|
||||
elif credit_days_based_on == "Last Day of the Next Month":
|
||||
due_date = (get_first_day(posting_date, 0, 2) + datetime.timedelta(-1)).strftime("%Y-%m-%d")
|
||||
template_name = get_pyt_term_template(party, party_type)
|
||||
if template_name:
|
||||
due_date = get_due_date_from_template(template_name, posting_date).strftime("%Y-%m-%d")
|
||||
else:
|
||||
if party_type == "Supplier":
|
||||
supplier_type = frappe.db.get_value(party_type, party, fieldname="supplier_type")
|
||||
template_name = frappe.db.get_value("Supplier Type", supplier_type, fieldname="payment_terms")
|
||||
if template_name:
|
||||
due_date = get_due_date_from_template(template_name, posting_date).strftime("%Y-%m-%d")
|
||||
|
||||
return due_date
|
||||
|
||||
def get_credit_days(party_type, party, company):
|
||||
credit_days = 0
|
||||
if party_type and party:
|
||||
if party_type == "Customer":
|
||||
credit_days_based_on, credit_days, customer_group = \
|
||||
frappe.db.get_value(party_type, party, ["credit_days_based_on", "credit_days", "customer_group"])
|
||||
|
||||
def get_due_date_from_template(template_name, posting_date):
|
||||
"""
|
||||
Inspects all `Payment Term`s from the a `Payment Terms Template` and returns the due
|
||||
date after considering all the `Payment Term`s requirements.
|
||||
:param template_name: Name of the `Payment Terms Template`
|
||||
:return: String representing the calculated due date
|
||||
"""
|
||||
due_date = getdate(posting_date)
|
||||
template = frappe.get_doc('Payment Terms Template', template_name)
|
||||
|
||||
for term in template.terms:
|
||||
if term.due_date_based_on == 'Day(s) after invoice date':
|
||||
due_date = max(due_date, add_days(due_date, term.credit_days))
|
||||
elif term.due_date_based_on == 'Day(s) after the end of the invoice month':
|
||||
due_date = max(due_date, add_days(get_last_day(due_date), term.credit_days))
|
||||
else:
|
||||
credit_days_based_on, credit_days, supplier_type = \
|
||||
frappe.db.get_value(party_type, party, ["credit_days_based_on", "credit_days", "supplier_type"])
|
||||
due_date = max(due_date, add_months(get_last_day(due_date), term.credit_months))
|
||||
|
||||
if not credit_days_based_on:
|
||||
if party_type == "Customer" and customer_group:
|
||||
credit_days_based_on, credit_days = \
|
||||
frappe.db.get_value("Customer Group", customer_group, ["credit_days_based_on", "credit_days"])
|
||||
elif party_type == "Supplier" and supplier_type:
|
||||
credit_days_based_on, credit_days = \
|
||||
frappe.db.get_value("Supplier Type", supplier_type, ["credit_days_based_on", "credit_days"])
|
||||
return due_date
|
||||
|
||||
if not credit_days_based_on:
|
||||
credit_days_based_on, credit_days = \
|
||||
frappe.db.get_value("Company", company, ["credit_days_based_on", "credit_days"])
|
||||
|
||||
return credit_days_based_on, credit_days
|
||||
|
||||
def validate_due_date(posting_date, due_date, party_type, party, company):
|
||||
def validate_due_date(posting_date, due_date, party_type, party):
|
||||
if getdate(due_date) < getdate(posting_date):
|
||||
frappe.throw(_("Due Date cannot be before Posting Date"))
|
||||
else:
|
||||
default_due_date = get_due_date(posting_date, party_type, party, company)
|
||||
default_due_date = get_due_date(posting_date, party_type, party)
|
||||
if not default_due_date:
|
||||
return
|
||||
|
||||
@ -316,7 +320,8 @@ def validate_due_date(posting_date, due_date, party_type, party, company):
|
||||
msgprint(_("Note: Due / Reference Date exceeds allowed customer credit days by {0} day(s)")
|
||||
.format(date_diff(due_date, default_due_date)))
|
||||
else:
|
||||
frappe.throw(_("Due / Reference Date cannot be after {0}").format(formatdate(default_due_date)))
|
||||
frappe.throw(_("Due / Reference Date cannot be after {0}")
|
||||
.format(formatdate(default_due_date)))
|
||||
|
||||
@frappe.whitelist()
|
||||
def set_taxes(party, party_type, posting_date, company, customer_group=None, supplier_type=None,
|
||||
@ -353,6 +358,16 @@ def set_taxes(party, party_type, posting_date, company, customer_group=None, sup
|
||||
|
||||
return get_tax_template(posting_date, args)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_pyt_term_template(party_name, party_type):
|
||||
template = None
|
||||
if party_type in ('Customer', 'Supplier'):
|
||||
template = frappe.db.get_value(party_type, party_name, fieldname='payment_terms')
|
||||
|
||||
return template
|
||||
|
||||
|
||||
def validate_party_frozen_disabled(party_type, party_name):
|
||||
if party_type and party_name:
|
||||
if party_type in ("Customer", "Supplier"):
|
||||
|
@ -113,7 +113,7 @@ class ReceivablePayableReport(object):
|
||||
row += [self.get_party_name(gle.party_type, gle.party)]
|
||||
|
||||
# get due date
|
||||
due_date = voucher_details.get(gle.voucher_no, {}).get("due_date", "")
|
||||
due_date = gle.due_date or voucher_details.get(gle.voucher_no, {}).get("due_date", "")
|
||||
|
||||
row += [gle.voucher_type, gle.voucher_no, due_date]
|
||||
|
||||
@ -162,8 +162,7 @@ class ReceivablePayableReport(object):
|
||||
|
||||
def get_entries_till(self, report_date, party_type):
|
||||
# returns a generator
|
||||
return (e for e in self.get_gl_entries(party_type)
|
||||
if getdate(e.posting_date) <= report_date)
|
||||
return (e for e in self.get_gl_entries(party_type) if getdate(e.posting_date) <= report_date)
|
||||
|
||||
def is_receivable_or_payable(self, gle, dr_or_cr, future_vouchers):
|
||||
return (
|
||||
@ -189,7 +188,8 @@ class ReceivablePayableReport(object):
|
||||
reverse_dr_or_cr = "credit" if dr_or_cr=="debit" else "debit"
|
||||
|
||||
for e in self.get_gl_entries_for(gle.party, gle.party_type, gle.voucher_type, gle.voucher_no):
|
||||
if getdate(e.posting_date) <= report_date and e.name!=gle.name:
|
||||
if getdate(e.posting_date) <= report_date and e.name!=gle.name \
|
||||
and (not gle.due_date or getdate(e.due_date) == getdate(gle.due_date)):
|
||||
amount = flt(e.get(reverse_dr_or_cr)) - flt(e.get(dr_or_cr))
|
||||
if e.voucher_no not in return_entries:
|
||||
payment_amount += amount
|
||||
@ -250,12 +250,12 @@ class ReceivablePayableReport(object):
|
||||
else:
|
||||
select_fields = "sum(debit) as debit, sum(credit) as credit"
|
||||
|
||||
self.gl_entries = frappe.db.sql("""select name, posting_date, account, party_type, party,
|
||||
voucher_type, voucher_no, against_voucher_type, against_voucher,
|
||||
self.gl_entries = frappe.db.sql("""select name, posting_date, account, party_type, party,
|
||||
voucher_type, voucher_no, against_voucher_type, against_voucher, due_date,
|
||||
account_currency, remarks, {0}
|
||||
from `tabGL Entry`
|
||||
where docstatus < 2 and party_type=%s and (party is not null and party != '') {1}
|
||||
group by voucher_type, voucher_no, against_voucher_type, against_voucher, party
|
||||
group by voucher_type, voucher_no, against_voucher_type, against_voucher, party, due_date
|
||||
order by posting_date, party"""
|
||||
.format(select_fields, conditions), values, as_dict=True)
|
||||
|
||||
|
@ -571,11 +571,12 @@ def get_stock_rbnb_difference(posting_date, company):
|
||||
# Amount should be credited
|
||||
return flt(stock_rbnb) + flt(sys_bal)
|
||||
|
||||
|
||||
def get_outstanding_invoices(party_type, party, account, condition=None):
|
||||
outstanding_invoices = []
|
||||
precision = frappe.get_precision("Sales Invoice", "outstanding_amount")
|
||||
|
||||
if party_type=="Customer":
|
||||
if party_type == "Customer":
|
||||
dr_or_cr = "debit_in_account_currency - credit_in_account_currency"
|
||||
payment_dr_or_cr = "payment_gl_entry.credit_in_account_currency - payment_gl_entry.debit_in_account_currency"
|
||||
else:
|
||||
@ -585,12 +586,7 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
|
||||
invoice = 'Sales Invoice' if party_type == 'Customer' else 'Purchase Invoice'
|
||||
invoice_list = frappe.db.sql("""
|
||||
select
|
||||
voucher_no, voucher_type, posting_date, ifnull(sum({dr_or_cr}), 0) as invoice_amount,
|
||||
(
|
||||
case when (voucher_type = 'Sales Invoice' or voucher_type = 'Purchase Invoice')
|
||||
then (select due_date from `tab{invoice}` where name = voucher_no)
|
||||
else posting_date end
|
||||
) as due_date,
|
||||
voucher_no, voucher_type, posting_date, ifnull(sum({dr_or_cr}), 0) as invoice_amount, due_date,
|
||||
(
|
||||
select ifnull(sum({payment_dr_or_cr}), 0)
|
||||
from `tabGL Entry` payment_gl_entry
|
||||
@ -601,6 +597,7 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
|
||||
and payment_gl_entry.party_type = invoice_gl_entry.party_type
|
||||
and payment_gl_entry.party = invoice_gl_entry.party
|
||||
and payment_gl_entry.account = invoice_gl_entry.account
|
||||
and payment_gl_entry.due_date = invoice_gl_entry.due_date
|
||||
and {payment_dr_or_cr} > 0
|
||||
) as payment_amount
|
||||
from
|
||||
@ -612,13 +609,13 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
|
||||
and ((voucher_type = 'Journal Entry'
|
||||
and (against_voucher = '' or against_voucher is null))
|
||||
or (voucher_type not in ('Journal Entry', 'Payment Entry')))
|
||||
group by voucher_type, voucher_no
|
||||
group by voucher_type, voucher_no, due_date
|
||||
having (invoice_amount - payment_amount) > 0.005
|
||||
order by posting_date, name""".format(
|
||||
dr_or_cr = dr_or_cr,
|
||||
invoice = invoice,
|
||||
payment_dr_or_cr = payment_dr_or_cr,
|
||||
condition = condition or ""
|
||||
order by posting_date, name, due_date""".format(
|
||||
dr_or_cr=dr_or_cr,
|
||||
invoice=invoice,
|
||||
payment_dr_or_cr=payment_dr_or_cr,
|
||||
condition=condition or ""
|
||||
), {
|
||||
"party_type": party_type,
|
||||
"party": party,
|
||||
@ -626,17 +623,24 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
|
||||
}, as_dict=True)
|
||||
|
||||
for d in invoice_list:
|
||||
outstanding_invoices.append(frappe._dict({
|
||||
'voucher_no': d.voucher_no,
|
||||
'voucher_type': d.voucher_type,
|
||||
'due_date': d.due_date,
|
||||
'posting_date': d.posting_date,
|
||||
'invoice_amount': flt(d.invoice_amount),
|
||||
'payment_amount': flt(d.payment_amount),
|
||||
'outstanding_amount': flt(d.invoice_amount - d.payment_amount, precision),
|
||||
'due_date': frappe.db.get_value(d.voucher_type, d.voucher_no,
|
||||
"posting_date" if party_type=="Employee" else "due_date"),
|
||||
}))
|
||||
due_date = d.due_date or (
|
||||
frappe.db.get_value(
|
||||
d.voucher_type, d.voucher_no,
|
||||
"posting_date" if party_type == "Employee" else "due_date"
|
||||
)
|
||||
)
|
||||
|
||||
outstanding_invoices.append(
|
||||
frappe._dict({
|
||||
'voucher_no': d.voucher_no,
|
||||
'voucher_type': d.voucher_type,
|
||||
'posting_date': d.posting_date,
|
||||
'invoice_amount': flt(d.invoice_amount),
|
||||
'payment_amount': flt(d.payment_amount),
|
||||
'outstanding_amount': flt(d.invoice_amount - d.payment_amount, precision),
|
||||
'due_date': due_date
|
||||
})
|
||||
)
|
||||
|
||||
outstanding_invoices = sorted(outstanding_invoices, key=lambda k: k['due_date'] or getdate(nowdate()))
|
||||
|
||||
|
@ -2440,6 +2440,98 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "payment_schedule_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Terms",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "payment_terms_template",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Terms Template",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Payment Terms Template",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "payment_schedule",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Schedule",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Payment Schedule",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@ -3172,7 +3264,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-11-15 01:03:44.591992",
|
||||
"modified": "2017-11-18 01:03:44.591992",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order",
|
||||
|
@ -8,6 +8,7 @@ import frappe.defaults
|
||||
from frappe.utils import flt, add_days, nowdate
|
||||
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt, make_purchase_invoice
|
||||
|
||||
|
||||
class TestPurchaseOrder(unittest.TestCase):
|
||||
def test_make_purchase_receipt(self):
|
||||
po = create_purchase_order(do_not_submit=True)
|
||||
@ -83,6 +84,33 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
self.assertEquals(pi.doctype, "Purchase Invoice")
|
||||
self.assertEquals(len(pi.get("items", [])), 1)
|
||||
|
||||
def test_make_purchase_invoice_with_terms(self):
|
||||
po = create_purchase_order(do_not_save=True)
|
||||
|
||||
self.assertRaises(frappe.ValidationError, make_purchase_invoice, po.name)
|
||||
|
||||
po.update(
|
||||
{"payment_terms_template": "_Test Payment Term Template"}
|
||||
)
|
||||
|
||||
po.save()
|
||||
po.submit()
|
||||
|
||||
self.assertEqual(po.payment_schedule[0].payment_amount, 2500.0)
|
||||
self.assertEqual(po.payment_schedule[0].due_date, po.transaction_date)
|
||||
self.assertEqual(po.payment_schedule[1].payment_amount, 2500.0)
|
||||
self.assertEqual(po.payment_schedule[1].due_date, add_days(po.transaction_date, 30))
|
||||
pi = make_purchase_invoice(po.name)
|
||||
pi.save()
|
||||
|
||||
self.assertEquals(pi.doctype, "Purchase Invoice")
|
||||
self.assertEquals(len(pi.get("items", [])), 1)
|
||||
|
||||
self.assertEqual(pi.payment_schedule[0].payment_amount, 2500.0)
|
||||
self.assertEqual(pi.payment_schedule[0].due_date, po.transaction_date)
|
||||
self.assertEqual(pi.payment_schedule[1].payment_amount, 2500.0)
|
||||
self.assertEqual(pi.payment_schedule[1].due_date, add_days(po.transaction_date, 30))
|
||||
|
||||
def test_subcontracting(self):
|
||||
po = create_purchase_order(item_code="_Test FG Item", is_subcontracted="Yes")
|
||||
self.assertEquals(len(po.get("supplied_items")), 2)
|
||||
@ -125,6 +153,35 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
"group_same_items": 1
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
def test_make_po_without_terms(self):
|
||||
po = create_purchase_order(do_not_save=1)
|
||||
|
||||
self.assertFalse(po.get('payment_schedule'))
|
||||
|
||||
po.insert()
|
||||
|
||||
self.assertTrue(po.get('payment_schedule'))
|
||||
|
||||
def test_terms_does_not_copy(self):
|
||||
po = create_purchase_order()
|
||||
|
||||
self.assertTrue(po.get('payment_schedule'))
|
||||
|
||||
pi = make_purchase_invoice(po.name)
|
||||
|
||||
self.assertFalse(pi.get('payment_schedule'))
|
||||
|
||||
def test_terms_copied(self):
|
||||
po = create_purchase_order(do_not_save=1)
|
||||
po.payment_terms_template = '_Test Payment Term Template'
|
||||
po.insert()
|
||||
po.submit()
|
||||
self.assertTrue(po.get('payment_schedule'))
|
||||
|
||||
pi = make_purchase_invoice(po.name)
|
||||
pi.insert()
|
||||
self.assertTrue(pi.get('payment_schedule'))
|
||||
|
||||
|
||||
def get_same_items():
|
||||
return [
|
||||
|
@ -597,8 +597,9 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "credit_days_based_on",
|
||||
"fieldtype": "Select",
|
||||
"depends_on": "",
|
||||
"fieldname": "payment_terms",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
@ -606,10 +607,10 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Credit Days Based On",
|
||||
"label": "Default Payment Terms Template",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "\nFixed Days\nLast Day of the Next Month",
|
||||
"options": "Payment Terms Template",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
@ -622,36 +623,6 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.credit_days_based_on == 'Fixed Days'",
|
||||
"fieldname": "credit_days",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Credit Days",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@ -970,8 +941,8 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-07-06 16:40:46.935608",
|
||||
"modified_by": "Administrator",
|
||||
"modified": "2017-08-31 16:10:44.049915",
|
||||
"modified_by": "tundebabzy@gmail.com",
|
||||
"module": "Buying",
|
||||
"name": "Supplier",
|
||||
"name_case": "Title Case",
|
||||
|
@ -1,4 +1,16 @@
|
||||
[
|
||||
{
|
||||
"doctype": "Supplier",
|
||||
"supplier_name": "_Test Supplier With Template 1",
|
||||
"supplier_type": "_Test Supplier Type",
|
||||
"payment_terms": "_Test Payment Term Template 3"
|
||||
},
|
||||
{
|
||||
"doctype": "Supplier",
|
||||
"supplier_name": "_Test Supplier P",
|
||||
"supplier_type": "_Test Supplier Type",
|
||||
"credit_days_based_on": "Fixed Days"
|
||||
},
|
||||
{
|
||||
"doctype": "Supplier",
|
||||
"supplier_name": "_Test Supplier with Country",
|
||||
|
@ -5,56 +5,62 @@ from __future__ import unicode_literals
|
||||
|
||||
import frappe, unittest
|
||||
from erpnext.accounts.party import get_due_date
|
||||
from erpnext.exceptions import PartyFrozen, PartyDisabled
|
||||
from erpnext.exceptions import PartyDisabled
|
||||
from frappe.test_runner import make_test_records
|
||||
|
||||
test_dependencies = ['Payment Term', 'Payment Terms Template']
|
||||
test_records = frappe.get_test_records('Supplier')
|
||||
|
||||
|
||||
class TestSupplier(unittest.TestCase):
|
||||
def test_supplier_due_date_against_supplier_credit_limit(self):
|
||||
# Set Credit Limit based on Fixed days
|
||||
frappe.db.set_value("Supplier", "_Test Supplier", "credit_days_based_on", "Fixed Days")
|
||||
frappe.db.set_value("Supplier", "_Test Supplier", "credit_days", 10)
|
||||
def test_supplier_default_payment_terms(self):
|
||||
# Payment Term based on Days after invoice date
|
||||
frappe.db.set_value(
|
||||
"Supplier", "_Test Supplier With Template 1", "payment_terms", "_Test Payment Term Template 3")
|
||||
|
||||
due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier", "_Test Company")
|
||||
self.assertEqual(due_date, "2016-02-01")
|
||||
due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier With Template 1")
|
||||
self.assertEqual(due_date, "2016-02-21")
|
||||
|
||||
# Set Credit Limit based on Last day next month
|
||||
frappe.db.set_value("Supplier", "_Test Supplier", "credit_days", 0)
|
||||
frappe.db.set_value("Supplier", "_Test Supplier", "credit_days_based_on",
|
||||
"Last Day of the Next Month")
|
||||
due_date = get_due_date("2017-01-22", "Supplier", "_Test Supplier With Template 1")
|
||||
self.assertEqual(due_date, "2017-02-21")
|
||||
|
||||
# Leap year
|
||||
due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier", "_Test Company")
|
||||
# Payment Term based on last day of month
|
||||
frappe.db.set_value(
|
||||
"Supplier", "_Test Supplier With Template 1", "payment_terms", "_Test Payment Term Template 1")
|
||||
|
||||
due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier With Template 1")
|
||||
self.assertEqual(due_date, "2016-02-29")
|
||||
# Non Leap year
|
||||
due_date = get_due_date("2017-01-22", "Supplier", "_Test Supplier", "_Test Company")
|
||||
|
||||
due_date = get_due_date("2017-01-22", "Supplier", "_Test Supplier With Template 1")
|
||||
self.assertEqual(due_date, "2017-02-28")
|
||||
|
||||
frappe.db.set_value("Supplier", "_Test Supplier", "credit_days_based_on", "")
|
||||
frappe.db.set_value("Supplier", "_Test Supplier With Template 1", "payment_terms", "")
|
||||
|
||||
# Set credit limit for the supplier type instead of supplier and evaluate the due date
|
||||
# based on Fixed days
|
||||
frappe.db.set_value("Supplier Type", "_Test Supplier Type", "credit_days_based_on",
|
||||
"Fixed Days")
|
||||
frappe.db.set_value("Supplier Type", "_Test Supplier Type", "credit_days", 10)
|
||||
frappe.db.set_value("Supplier Type", "_Test Supplier Type", "payment_terms", "_Test Payment Term Template 3")
|
||||
|
||||
due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier", "_Test Company")
|
||||
self.assertEqual(due_date, "2016-02-01")
|
||||
due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier With Template 1")
|
||||
self.assertEqual(due_date, "2016-02-21")
|
||||
|
||||
# Set credit limit for the supplier type instead of supplier and evaluate the due date
|
||||
# based on Last day of next month
|
||||
frappe.db.set_value("Supplier", "_Test Supplier Type", "credit_days", 0)
|
||||
frappe.db.set_value("Supplier Type", "_Test Supplier Type", "credit_days_based_on",
|
||||
"Last Day of the Next Month")
|
||||
# Payment terms for Supplier Type instead of supplier and evaluate the due date
|
||||
frappe.db.set_value("Supplier Type", "_Test Supplier Type", "payment_terms", "_Test Payment Term Template 1")
|
||||
|
||||
# Leap year
|
||||
due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier", "_Test Company")
|
||||
due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier With Template 1")
|
||||
self.assertEqual(due_date, "2016-02-29")
|
||||
# Non Leap year
|
||||
due_date = get_due_date("2017-01-22", "Supplier", "_Test Supplier", "_Test Company")
|
||||
# # Non Leap year
|
||||
due_date = get_due_date("2017-01-22", "Supplier", "_Test Supplier With Template 1")
|
||||
self.assertEqual(due_date, "2017-02-28")
|
||||
|
||||
# Supplier with no default Payment Terms Template
|
||||
frappe.db.set_value("Supplier Type", "_Test Supplier Type", "payment_terms", "")
|
||||
frappe.db.set_value("Supplier", "_Test Supplier", "payment_terms", "")
|
||||
|
||||
due_date = get_due_date("2016-01-22", "Supplier", "_Test Supplier")
|
||||
self.assertEqual(due_date, "2016-01-22")
|
||||
# # Non Leap year
|
||||
due_date = get_due_date("2017-01-22", "Supplier", "_Test Supplier")
|
||||
self.assertEqual(due_date, "2017-01-22")
|
||||
|
||||
def test_supplier_disabled(self):
|
||||
make_test_records("Item")
|
||||
@ -71,7 +77,6 @@ class TestSupplier(unittest.TestCase):
|
||||
|
||||
po.save()
|
||||
|
||||
|
||||
def test_supplier_country(self):
|
||||
# Test that country field exists in Supplier DocType
|
||||
supplier = frappe.get_doc('Supplier', '_Test Supplier with Country')
|
||||
|
@ -4,7 +4,7 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe, erpnext
|
||||
from frappe import _, throw
|
||||
from frappe.utils import today, flt, cint, fmt_money, formatdate, getdate
|
||||
from frappe.utils import today, flt, cint, fmt_money, formatdate, getdate, add_days, add_months, get_last_day
|
||||
from erpnext.setup.utils import get_exchange_rate
|
||||
from erpnext.accounts.utils import get_fiscal_years, validate_fiscal_year, get_account_currency
|
||||
from erpnext.utilities.transaction_base import TransactionBase
|
||||
@ -26,11 +26,19 @@ class AccountsController(TransactionBase):
|
||||
return self.__company_currency
|
||||
|
||||
def onload(self):
|
||||
self.get("__onload").make_payment_via_journal_entry = frappe.db.get_single_value('Accounts Settings', 'make_payment_via_journal_entry')
|
||||
self.get("__onload").make_payment_via_journal_entry \
|
||||
= frappe.db.get_single_value('Accounts Settings', 'make_payment_via_journal_entry')
|
||||
|
||||
if self.is_new():
|
||||
relevant_docs = ("Quotation", "Purchase Order", "Sales Order",
|
||||
"Purchase Invoice", "Sales Invoice")
|
||||
if self.doctype in relevant_docs:
|
||||
self.set_payment_schedule()
|
||||
|
||||
def validate(self):
|
||||
if self.get("_action") and self._action != "update_after_submit":
|
||||
self.set_missing_values(for_validate=True)
|
||||
|
||||
self.validate_date_with_fiscal_year()
|
||||
|
||||
if self.meta.get_field("currency"):
|
||||
@ -42,9 +50,7 @@ class AccountsController(TransactionBase):
|
||||
validate_return(self)
|
||||
self.set_total_in_words()
|
||||
|
||||
if self.doctype in ("Sales Invoice", "Purchase Invoice") and not self.is_return:
|
||||
self.validate_due_date()
|
||||
self.validate_advance_entries()
|
||||
self.validate_all_documents_schedule()
|
||||
|
||||
if self.meta.get_field("taxes_and_charges"):
|
||||
self.validate_enabled_taxes_and_charges()
|
||||
@ -55,6 +61,26 @@ class AccountsController(TransactionBase):
|
||||
if self.doctype == 'Purchase Invoice':
|
||||
self.validate_paid_amount()
|
||||
|
||||
def validate_invoice_documents_schedule(self):
|
||||
self.validate_payment_schedule_dates()
|
||||
self.set_due_date()
|
||||
self.validate_invoice_portion()
|
||||
self.set_payment_schedule()
|
||||
self.validate_payment_schedule_amount()
|
||||
self.validate_due_date()
|
||||
self.validate_advance_entries()
|
||||
|
||||
def validate_non_invoice_documents_schedule(self):
|
||||
self.validate_invoice_portion()
|
||||
self.set_payment_schedule()
|
||||
self.validate_payment_schedule_amount()
|
||||
|
||||
def validate_all_documents_schedule(self):
|
||||
if self.doctype in ("Sales Invoice", "Purchase Invoice") and not self.is_return:
|
||||
self.validate_invoice_documents_schedule()
|
||||
elif self.doctype in ("Quotation", "Purchase Order", "Sales Order"):
|
||||
self.validate_non_invoice_documents_schedule()
|
||||
|
||||
def before_print(self):
|
||||
if self.doctype in ['Purchase Order', 'Sales Order']:
|
||||
if self.get("group_same_items"):
|
||||
@ -74,11 +100,11 @@ class AccountsController(TransactionBase):
|
||||
self.paid_amount = 0
|
||||
frappe.throw(_("Note: Payment Entry will not be created since 'Cash or Bank Account' was not specified"))
|
||||
else:
|
||||
frappe.db.set(self,'paid_amount',0)
|
||||
frappe.db.set(self, 'paid_amount', 0)
|
||||
|
||||
def set_missing_values(self, for_validate=False):
|
||||
if frappe.flags.in_test:
|
||||
for fieldname in ["posting_date","transaction_date"]:
|
||||
for fieldname in ["posting_date", "transaction_date"]:
|
||||
if self.meta.get_field(fieldname) and not self.get(fieldname):
|
||||
self.set(fieldname, today())
|
||||
break
|
||||
@ -109,9 +135,9 @@ class AccountsController(TransactionBase):
|
||||
if not self.due_date:
|
||||
frappe.throw(_("Due Date is mandatory"))
|
||||
|
||||
validate_due_date(self.posting_date, self.due_date, "Customer", self.customer, self.company)
|
||||
validate_due_date(self.posting_date, self.due_date, "Customer", self.customer)
|
||||
elif self.doctype == "Purchase Invoice":
|
||||
validate_due_date(self.posting_date, self.due_date, "Supplier", self.supplier, self.company)
|
||||
validate_due_date(self.posting_date, self.due_date, "Supplier", self.supplier)
|
||||
|
||||
def set_price_list_currency(self, buying_or_selling):
|
||||
if self.meta.get_field("posting_date"):
|
||||
@ -619,6 +645,67 @@ class AccountsController(TransactionBase):
|
||||
for item in duplicate_list:
|
||||
self.remove(item)
|
||||
|
||||
def set_payment_schedule(self):
|
||||
posting_date = self.get("posting_date") or self.get("transaction_date")
|
||||
date = self.get("due_date")
|
||||
due_date = date or posting_date
|
||||
grand_total = self.get("rounded_total") or self.grand_total
|
||||
|
||||
if not self.get("payment_schedule"):
|
||||
if self.get("payment_terms_template"):
|
||||
data = get_payment_terms(self.payment_terms_template, posting_date, grand_total)
|
||||
for item in data:
|
||||
self.append("payment_schedule", item)
|
||||
else:
|
||||
data = dict(due_date=due_date, invoice_portion=100, payment_amount=grand_total)
|
||||
self.append("payment_schedule", data)
|
||||
else:
|
||||
for d in self.get("payment_schedule"):
|
||||
d.payment_amount = grand_total * flt(d.invoice_portion) / 100
|
||||
|
||||
def set_due_date(self):
|
||||
due_dates = [d.due_date for d in self.get("payment_schedule") if d.due_date]
|
||||
if due_dates:
|
||||
self.due_date = max(due_dates)
|
||||
|
||||
def validate_payment_schedule_dates(self):
|
||||
dates = []
|
||||
li = []
|
||||
if self.due_date and getdate(self.due_date) < getdate(self.posting_date):
|
||||
frappe.throw(_("Due Date cannot be before posting date"))
|
||||
|
||||
for d in self.get("payment_schedule"):
|
||||
if getdate(d.due_date) < getdate(self.posting_date):
|
||||
frappe.throw(_("Row {0}: Due Date cannot be before posting date").format(d.idx))
|
||||
elif d.due_date in dates:
|
||||
li.append('{0} in row {1}'.format(d.due_date, d.idx))
|
||||
# frappe.throw(_("Row {0}: Duplicate due date found").format(d.idx))
|
||||
dates.append(d.due_date)
|
||||
|
||||
if li:
|
||||
duplicates = '<br>' + '<br>'.join(li)
|
||||
frappe.throw(_("Rows with duplicate due dates in other rows were found: {list}")
|
||||
.format(list=duplicates))
|
||||
|
||||
def validate_payment_schedule_amount(self):
|
||||
if self.get("payment_schedule"):
|
||||
total = 0
|
||||
for d in self.get("payment_schedule"):
|
||||
total += flt(d.payment_amount)
|
||||
|
||||
grand_total = self.get("rounded_total") or self.grand_total
|
||||
if total != grand_total:
|
||||
frappe.throw(_("Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total"))
|
||||
|
||||
def validate_invoice_portion(self):
|
||||
if self.get("payment_schedule"):
|
||||
total_portion = 0
|
||||
for term in self.payment_schedule:
|
||||
total_portion += flt(term.get('invoice_portion', 0))
|
||||
|
||||
if flt(total_portion, 2) != 100.00:
|
||||
frappe.throw(_('Combined invoice portion must equal 100%'), indicator='red')
|
||||
|
||||
def is_rounded_total_disabled(self):
|
||||
if self.meta.get_field("disable_rounded_total"):
|
||||
return self.disable_rounded_total
|
||||
@ -796,4 +883,43 @@ def update_invoice_status():
|
||||
where due_date < CURDATE() and docstatus = 1 and outstanding_amount > 0""")
|
||||
|
||||
frappe.db.sql(""" update `tabPurchase Invoice` set status = 'Overdue'
|
||||
where due_date < CURDATE() and docstatus = 1 and outstanding_amount > 0""")
|
||||
where due_date < CURDATE() and docstatus = 1 and outstanding_amount > 0""")
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_payment_terms(terms_template, posting_date=None, grand_total=None):
|
||||
if not terms_template:
|
||||
return
|
||||
|
||||
terms_doc = frappe.get_doc("Payment Terms Template", terms_template)
|
||||
|
||||
schedule = []
|
||||
for d in terms_doc.get("terms"):
|
||||
term_details = get_payment_term_details(d, posting_date, grand_total)
|
||||
schedule.append(term_details)
|
||||
|
||||
return schedule
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_payment_term_details(term, posting_date=None, grand_total=None):
|
||||
term_details = frappe._dict()
|
||||
if isinstance(term, unicode):
|
||||
term = frappe.get_doc("Payment Term", term)
|
||||
else:
|
||||
term_details.payment_term = term.payment_term
|
||||
term_details.description = term.description
|
||||
term_details.invoice_portion = term.invoice_portion
|
||||
term_details.payment_amount = flt(term.invoice_portion) * flt(grand_total) / 100
|
||||
if posting_date:
|
||||
term_details.due_date = get_due_date(posting_date, term)
|
||||
return term_details
|
||||
|
||||
def get_due_date(posting_date, term):
|
||||
due_date = None
|
||||
if term.due_date_based_on == "Day(s) after invoice date":
|
||||
due_date = add_days(posting_date, term.credit_days)
|
||||
elif term.due_date_based_on == "Day(s) after the end of the invoice month":
|
||||
due_date = add_days(get_last_day(posting_date), term.credit_days)
|
||||
elif term.due_date_based_on == "Month(s) after the end of the invoice month":
|
||||
due_date = add_months(get_last_day(posting_date), term.credit_months)
|
||||
|
||||
return due_date
|
||||
|
@ -256,6 +256,9 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
||||
target_doc.dn_detail = source_doc.dn_detail
|
||||
target_doc.expense_account = source_doc.expense_account
|
||||
|
||||
def update_terms(source_doc, target_doc, source_parent):
|
||||
target_doc.payment_amount = -source_doc.payment_amount
|
||||
|
||||
doclist = get_mapped_doc(doctype, source_name, {
|
||||
doctype: {
|
||||
"doctype": doctype,
|
||||
@ -272,6 +275,10 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
||||
},
|
||||
"postprocess": update_item
|
||||
},
|
||||
"Payment Schedule": {
|
||||
"doctype": "Payment Schedule",
|
||||
"postprocess": update_terms
|
||||
}
|
||||
}, target_doc, set_missing_values)
|
||||
|
||||
return doclist
|
||||
|
70
erpnext/docs/user/manual/en/accounts/payment-terms.md
Normal file
70
erpnext/docs/user/manual/en/accounts/payment-terms.md
Normal file
@ -0,0 +1,70 @@
|
||||
# Payment Terms
|
||||
You can save your business' payment terms on ERPNext and include it in all documents in the sales/purchase cycle and ERPNext will make all the proper general ledger entries accordingly.
|
||||
|
||||
The documents you can attach Payment Terms to are:
|
||||
- Sales Invoice
|
||||
- Purchase Invoice
|
||||
- Sales Order
|
||||
- Purchase Order
|
||||
- Quotation
|
||||
|
||||
Note that the introduction of Payment Terms removes "Credit Days" and "Credit Days Based On" fields in Customer/Supplier master. Payment Term contains the same information and makes it more flexible to use.
|
||||
|
||||
## Payment Terms
|
||||
Navigate to the Payment Term list page and click "New".
|
||||
> Accounts > Payment Term > New Payment Term
|
||||
|
||||
Payment Term has the following fields:
|
||||
**Payment Term Name:** (optional) The name for this Payment Term.
|
||||
|
||||
**Due Date Based On:** The basis by which the due date for the Payment Term is to be calculated. There are three options:
|
||||
- Day(s) after invoice date: Due date should be calculated in days with reference to the posting date of the invoice
|
||||
- Day(s) after the end of the invoice month: Due date should be calculated in days with reference to the last day of the month in which the invoice was created
|
||||
- Month(s) after the end of the invoice month: Due date should be calculated in months with reference to the last day of the month in which the invoice was created
|
||||
|
||||
**Invoice Portion:** (optional) The portion of the total invoice amount for which this Payment Term should be applied. Value given will be regarded as percentage i.e 100 = 100%
|
||||
|
||||
**Credit Days:** (optional) The number of days or month credit is allowed depending on the option chosen in the `Due Date Based On` field. 0 means no credit allowed.
|
||||
|
||||
**Description:** (optional) A brief description of the Payment Term.
|
||||
|
||||
## Payment Terms In Converted Documents
|
||||
When converting or copying documents in the sales/purchase cycle, the attached Payment Term(s) will not be copied. The reason for this is because the copied information might no longer be true. For example, a Quotation is created for a service costing $1000 on January 1 with payment term of "N 30" (Net payable within 30 days) and then sent to a customer. On the quotation, the due date on the invoice will be January 30. Let's say the customer agrees to the quotation of January 20 and you decide to make an invoice from the Quotation. If the Payment Terms from the Quotation is copied, the due date on the invoice will still wrongly read January 30. This issue also applies for recurring documents.
|
||||
|
||||
This does not mean you have manually set Payment Terms for every document. If you want the Payment Terms to be copyable, make use of Payment Terms Template.
|
||||
|
||||
## Payment Terms Template
|
||||
Payment Terms Template tells ERPNext how to populate the table in the Payment Terms Schedule section of the sales/purchase document.
|
||||
|
||||
You should use it if you have a set of standard Payment Terms or if you want the Payment Term(s) on a sales/purchase document to be copyable.
|
||||
|
||||
To create a new Payment Terms Template, navigate to the Payment Term Template creation form:
|
||||
> Accounts > Payment Terms Template > New Payment Terms Template
|
||||
|
||||
**Payment Term:** (optional) The name for this Payment Term.
|
||||
|
||||
**Due Date Based On:** The basis by which the due date for the Payment Term is to be calculated. There are three options:
|
||||
- Day(s) after invoice date: Due date should be calculated in days with reference to the posting date of the invoice
|
||||
- Day(s) after the end of the invoice month: Due date should be calculated in days with reference to the last day of the month in which the invoice was created
|
||||
- Month(s) after the end of the invoice month: Due date should be calculated in months with reference to the last day of the month in which the invoice was created
|
||||
|
||||
**Invoice Portion:** (optional) The portion of the total invoice amount for which this Payment Term should be applied. Value given will be regarded as percentage i.e 100 = 100%
|
||||
|
||||
**Credit Days:** (optional) The number of days or month credit is allowed depending on the option chosen in the `Due Date Based On` field. 0 means no credit allowed.
|
||||
|
||||
**Description:** (optional) A brief description of the Payment Term.
|
||||
|
||||
Add as many rows as needed but make sure that the sum of the values in the `Invoice Portion` fields in all populated rows equals 100.
|
||||
|
||||
## How to Add Payment Terms To Documents
|
||||
You can add Payments Terms in the "Payment Terms Schedule" section of the Document. Each row in the table represents a portion of the document's grand total. The table collects the following information:
|
||||
|
||||
**Payment Term:** (optional) The name of the Payment Term document you require. If this is added, the data from the selected Payment Term will be used to populate the remaining columns in the row.
|
||||
|
||||
**Description:** (optional) Description of the Payment Term.
|
||||
|
||||
**Due Date:** (optional) The due date for the portion of the invoice. Set this value only if you did not specify anything in the `Payment Term` column.
|
||||
|
||||
**Invoice Portion:** The percentage portion of the document represented in each row.
|
||||
|
||||
**Payment Amount:** The amount due from the portion of the invoice represented by each row.
|
@ -8,6 +8,7 @@ from frappe.utils import random_string, nowdate
|
||||
from erpnext.hr.doctype.expense_claim.expense_claim import make_bank_entry
|
||||
|
||||
test_records = frappe.get_test_records('Expense Claim')
|
||||
test_dependencies = ['Employee']
|
||||
|
||||
class TestExpenseClaim(unittest.TestCase):
|
||||
def test_total_expense_claim_for_project(self):
|
||||
|
29
erpnext/hub_node/api.py
Normal file
29
erpnext/hub_node/api.py
Normal file
@ -0,0 +1,29 @@
|
||||
# Copyright (c) 2015, Web Notes Technologies Pvt. Ltd. and Contributors and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
|
||||
import frappe, json
|
||||
from frappe.utils import now, nowdate
|
||||
from erpnext.hub_node.doctype.hub_settings.hub_settings import get_hub_settings
|
||||
|
||||
# API wrapper
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def call_method(access_token, method, message):
|
||||
try:
|
||||
args = json.loads(message)
|
||||
if args:
|
||||
return globals()[method](access_token, args)
|
||||
else:
|
||||
return globals()[method](access_token)
|
||||
except:
|
||||
print("Client Exception")
|
||||
print(frappe.get_traceback())
|
||||
|
||||
def disable_and_suspend_hub_user(access_token):
|
||||
hub_settings = get_hub_settings()
|
||||
hub_settings.publish = 0
|
||||
hub_settings.publish_pricing = 0
|
||||
hub_settings.publish_availability = 0
|
||||
hub_settings.suspended = 1
|
||||
hub_settings.enabled = 0
|
||||
hub_settings.save(ignore_permissions=True)
|
@ -16,7 +16,7 @@ QUnit.test("test: production order", function (assert) {
|
||||
frappe.run_serially([
|
||||
// test production order
|
||||
() => frappe.set_route("List", "Production Order"),
|
||||
() => frappe.timeout(0.5),
|
||||
() => frappe.timeout(3),
|
||||
|
||||
// Create a laptop production order
|
||||
() => {
|
||||
@ -29,7 +29,7 @@ QUnit.test("test: production order", function (assert) {
|
||||
{fg_warehouse: "Finished Goods - FT"}
|
||||
]);
|
||||
},
|
||||
() => frappe.timeout(2),
|
||||
() => frappe.timeout(3),
|
||||
() => {
|
||||
assert.equal(cur_frm.doc.planned_operating_cost, cur_frm.doc.total_operating_cost,
|
||||
"Total and Planned Cost is equal");
|
||||
@ -54,7 +54,7 @@ QUnit.test("test: production order", function (assert) {
|
||||
() => cur_frm.savesubmit(),
|
||||
() => frappe.timeout(1),
|
||||
() => frappe.click_button('Yes'),
|
||||
() => frappe.timeout(1),
|
||||
() => frappe.timeout(2.5),
|
||||
|
||||
// Confirm the production order timesheet, save and submit it
|
||||
() => frappe.click_link("TS-00"),
|
||||
@ -62,12 +62,10 @@ QUnit.test("test: production order", function (assert) {
|
||||
() => frappe.click_button("Submit"),
|
||||
() => frappe.timeout(1),
|
||||
() => frappe.click_button("Yes"),
|
||||
() => frappe.timeout(2),
|
||||
() => frappe.timeout(2.5),
|
||||
|
||||
// Start the production order process
|
||||
() => frappe.set_route("List", "Production Order"),
|
||||
() => frappe.timeout(.5),
|
||||
() => frappe.set_route("List", "Production Order"),
|
||||
() => frappe.timeout(2),
|
||||
() => frappe.click_link("Laptop"),
|
||||
() => frappe.timeout(1),
|
||||
|
@ -460,6 +460,8 @@ execute:frappe.delete_doc_if_exists("DocType", "Program Fee")
|
||||
erpnext.patches.v9_0.set_pos_profile_name
|
||||
erpnext.patches.v9_0.remove_non_existing_warehouse_from_stock_settings
|
||||
execute:frappe.delete_doc_if_exists("DocType", "Program Fee")
|
||||
erpnext.patches.v8_10.update_gl_due_date_for_pi_and_si
|
||||
erpnext.patches.v8_10.change_default_customer_credit_days
|
||||
erpnext.patches.v9_0.update_employee_loan_details
|
||||
erpnext.patches.v9_2.delete_healthcare_domain_default_items
|
||||
erpnext.patches.v9_1.create_issue_opportunity_type
|
||||
|
0
erpnext/patches/v8_10/__init__.py
Normal file
0
erpnext/patches/v8_10/__init__.py
Normal file
88
erpnext/patches/v8_10/change_default_customer_credit_days.py
Normal file
88
erpnext/patches/v8_10/change_default_customer_credit_days.py
Normal file
@ -0,0 +1,88 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("selling", "doctype", "customer")
|
||||
frappe.reload_doc("buying", "doctype", "supplier")
|
||||
frappe.reload_doc("setup", "doctype", "supplier_type")
|
||||
frappe.reload_doc("accounts", "doctype", "payment_term")
|
||||
frappe.reload_doc("accounts", "doctype", "payment_terms_template_detail")
|
||||
frappe.reload_doc("accounts", "doctype", "payment_terms_template")
|
||||
|
||||
payment_terms = []
|
||||
records = []
|
||||
for doctype in ("Customer", "Supplier", "Supplier Type"):
|
||||
credit_days = frappe.db.sql("""
|
||||
SELECT DISTINCT `credit_days`, `credit_days_based_on`, `name`
|
||||
from `tab{0}`
|
||||
where
|
||||
(credit_days_based_on='Fixed Days' and credit_days is not null)
|
||||
or credit_days_based_on='Last Day of the Next Month'
|
||||
""".format(doctype))
|
||||
|
||||
credit_records = ((record[0], record[1], record[2]) for record in credit_days)
|
||||
for days, based_on, party_name in credit_records:
|
||||
if based_on == "Fixed Days":
|
||||
pyt_template_name = 'Default Payment Term - N{0}'.format(days)
|
||||
else:
|
||||
pyt_template_name = 'Default Payment Term - EO2M'
|
||||
|
||||
if not frappe.db.exists("Payment Terms Template", pyt_template_name):
|
||||
payment_term = make_payment_term(days, based_on)
|
||||
template = make_template(payment_term)
|
||||
else:
|
||||
template = frappe.get_doc("Payment Terms Template", pyt_template_name)
|
||||
|
||||
payment_terms.append('WHEN `name`="%s" THEN "%s"' % (party_name, template.template_name))
|
||||
records.append(party_name)
|
||||
|
||||
begin_query_str = "UPDATE `tab{0}` SET `payment_terms` = CASE ".format(doctype)
|
||||
value_query_str = " ".join(payment_terms)
|
||||
cond_query_str = " ELSE `payment_terms` END WHERE "
|
||||
|
||||
if records:
|
||||
frappe.db.sql(
|
||||
begin_query_str + value_query_str + cond_query_str + '`name` IN %s',
|
||||
(records,)
|
||||
)
|
||||
|
||||
|
||||
def make_template(payment_term):
|
||||
doc = frappe.new_doc('Payment Terms Template Detail')
|
||||
doc.payment_term = payment_term.payment_term_name
|
||||
doc.due_date_based_on = payment_term.due_date_based_on
|
||||
doc.invoice_portion = payment_term.invoice_portion
|
||||
doc.description = payment_term.description
|
||||
doc.credit_days = payment_term.credit_days
|
||||
doc.credit_months = payment_term.credit_months
|
||||
|
||||
template = frappe.new_doc('Payment Terms Template')
|
||||
template.template_name = 'Default Payment Term - {0}'.format(payment_term.payment_term_name)
|
||||
template.append('terms', doc)
|
||||
template.save()
|
||||
|
||||
return template
|
||||
|
||||
|
||||
def make_payment_term(days, based_on):
|
||||
based_on_map = {
|
||||
'Fixed Days': 'Day(s) after invoice date',
|
||||
'Last Day of the Next Month': 'Month(s) after the end of the invoice month'
|
||||
}
|
||||
|
||||
doc = frappe.new_doc('Payment Term')
|
||||
doc.due_date_based_on = based_on_map.get(based_on)
|
||||
doc.invoice_portion = 100
|
||||
|
||||
if based_on == 'Fixed Days':
|
||||
doc.credit_days = days
|
||||
doc.description = 'Net payable within {0} days'.format(days)
|
||||
doc.payment_term_name = 'N{0}'.format(days)
|
||||
else:
|
||||
doc.credit_months = 1
|
||||
doc.description = 'Net payable by the end of next month'
|
||||
doc.payment_term_name = 'EO2M'
|
||||
|
||||
doc.save()
|
||||
return doc
|
138
erpnext/patches/v8_10/update_gl_due_date_for_pi_and_si.py
Normal file
138
erpnext/patches/v8_10/update_gl_due_date_for_pi_and_si.py
Normal file
@ -0,0 +1,138 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
# This will update existing GL Entries by saving its linked Purchase/Sales Invoice's
|
||||
# Journal Entry's due date as the due date for the GL Entry
|
||||
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("accounts", "doctype", "gl_entry")
|
||||
|
||||
kwargs = get_query_kwargs()
|
||||
|
||||
for kwarg in kwargs:
|
||||
for batch in get_result_in_batches(**kwarg):
|
||||
voucher_num_col = kwarg.get('voucher_num_col', 'voucher_no')
|
||||
voucher_type = kwarg.get('use_voucher_type') or kwarg.get('voucher_type')
|
||||
conditions, names = build_conditions(batch, voucher_type, voucher_num_col)
|
||||
if conditions and names:
|
||||
start = 'UPDATE `tabGL Entry` SET `due_date` = CASE '
|
||||
cond = ' '.join(conditions)
|
||||
else_cond = ' ELSE `due_date` END WHERE '
|
||||
|
||||
frappe.db.sql(
|
||||
start + cond + else_cond + voucher_num_col + ' IN %s',
|
||||
values=(names,)
|
||||
)
|
||||
|
||||
|
||||
def get_result_in_batches(**kwargs):
|
||||
"""A simple generator to yield slices of GL Entry records"""
|
||||
while True:
|
||||
batch = get_gle_batch(**kwargs)
|
||||
if batch:
|
||||
yield batch
|
||||
else:
|
||||
return
|
||||
|
||||
|
||||
def get_gle_batch(**kwargs):
|
||||
"""Returns a slice of records in GL Entry"""
|
||||
doctype = kwargs.get('doctype')
|
||||
fields = kwargs.get('fields')
|
||||
limit_start = kwargs.get('limit_start')
|
||||
limit_page_length = kwargs.get('limit_page_length')
|
||||
filters = kwargs.get('filters')
|
||||
or_filters = kwargs.get('or_filters')
|
||||
|
||||
results = frappe.get_list(
|
||||
doctype, fields=fields, limit_start=limit_start, limit_page_length=limit_page_length,
|
||||
filters=filters, or_filters=or_filters
|
||||
)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def build_conditions(query_results, voucher_type, voucher_num_col):
|
||||
"""
|
||||
builds the string to be used is sql CASE statement. Returns the a tuple of
|
||||
the string for the CASE statement and a tuple of applicable voucher names
|
||||
"""
|
||||
conditions = []
|
||||
invoice_names = []
|
||||
|
||||
for result in query_results:
|
||||
voucher_no = result.get(voucher_num_col)
|
||||
if voucher_no:
|
||||
invoice_names.append("%s" % (voucher_no,))
|
||||
|
||||
# get invoice details
|
||||
invoice_details = frappe.get_list(
|
||||
voucher_type, fields=['name', 'due_date'], filters={'name': ('in', invoice_names)}
|
||||
)
|
||||
|
||||
if invoice_details:
|
||||
for d in invoice_details:
|
||||
conditions.append('WHEN `{voucher_no}`="{number}" THEN "{date}"'.format(
|
||||
number=d.name, date=d.due_date, voucher_no=voucher_num_col))
|
||||
|
||||
return conditions, invoice_names
|
||||
|
||||
|
||||
def get_query_kwargs():
|
||||
pi_kwargs = dict(
|
||||
voucher_type='Purchase Invoice', doctype='GL Entry', fields=['voucher_no'],
|
||||
limit_start=0, limit_page_length=5, filters={
|
||||
"ifnull(due_date, '')": ('=', ''), "ifnull(party, '')": ('!=', ''),
|
||||
'voucher_type': 'Purchase Invoice', 'credit': ('!=', '0')
|
||||
}
|
||||
)
|
||||
|
||||
si_kwargs = dict(
|
||||
voucher_type='Sales Invoice', doctype='GL Entry', fields=['voucher_no'],
|
||||
limit_start=0, limit_page_length=5, filters={
|
||||
"ifnull(due_date, '')": ('=', ''), "ifnull(party, '')": ('!=', ''),
|
||||
'voucher_type': 'Sales Invoice', 'debit': ('!=', '0')
|
||||
}
|
||||
)
|
||||
|
||||
journal_kwargs_si = dict(
|
||||
voucher_type='Journal Entry', doctype='GL Entry', fields=['against_voucher'],
|
||||
limit_start=0, limit_page_length=5, filters={
|
||||
"ifnull(due_date, '')": ('=', ''), "ifnull(party, '')": ('!=', ''),
|
||||
'voucher_type': 'Journal Entry', 'against_voucher_type': 'Sales Invoice'
|
||||
},
|
||||
voucher_num_col='against_voucher', use_voucher_type='Sales Invoice',
|
||||
)
|
||||
|
||||
journal_kwargs_pi = dict(
|
||||
voucher_type='Journal Entry', doctype='GL Entry', fields=['against_voucher'],
|
||||
limit_start=0, limit_page_length=5, filters={
|
||||
"ifnull(due_date, '')": ('=', ''), "ifnull(party, '')": ('!=', ''),
|
||||
'voucher_type': 'Journal Entry', 'against_voucher_type': 'Purchase Invoice'
|
||||
},
|
||||
voucher_num_col='against_voucher', use_voucher_type='Purchase Invoice',
|
||||
)
|
||||
|
||||
payment_entry_kwargs_pi = dict(
|
||||
voucher_type='Payment Entry', doctype='GL Entry', fields=['against_voucher'],
|
||||
limit_start=0, limit_page_length=5, filters={
|
||||
"ifnull(due_date, '')": ('=', ''), "ifnull(party, '')": ('!=', ''),
|
||||
'voucher_type': 'Payment Entry', 'against_voucher_type': 'Purchase Invoice'
|
||||
},
|
||||
voucher_num_col='against_voucher', use_voucher_type='Purchase Invoice',
|
||||
)
|
||||
|
||||
payment_entry_kwargs_si = dict(
|
||||
voucher_type='Payment Entry', doctype='GL Entry', fields=['against_voucher'],
|
||||
limit_start=0, limit_page_length=5, filters={
|
||||
"ifnull(due_date, '')": ('=', ''), "ifnull(party, '')": ('!=', ''),
|
||||
'voucher_type': 'Payment Entry', 'against_voucher_type': 'Sales Invoice'
|
||||
},
|
||||
voucher_num_col='against_voucher', use_voucher_type='Sales Invoice',
|
||||
)
|
||||
|
||||
return [
|
||||
pi_kwargs, si_kwargs, journal_kwargs_pi, journal_kwargs_si,
|
||||
payment_entry_kwargs_pi, payment_entry_kwargs_si
|
||||
]
|
@ -62,15 +62,32 @@ frappe.ui.form.on('Sales Invoice Payment', {
|
||||
frappe.model.set_value(cdt, cdn, 'account', account)
|
||||
})
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Sales Invoice", {
|
||||
payment_terms_template: function() {
|
||||
cur_frm.trigger("disable_due_date");
|
||||
}
|
||||
});
|
||||
|
||||
frappe.ui.form.on('Purchase Invoice', {
|
||||
mode_of_payment: function(frm) {
|
||||
get_payment_mode_account(frm, frm.doc.mode_of_payment, function(account){
|
||||
frm.set_value('cash_bank_account', account);
|
||||
})
|
||||
},
|
||||
|
||||
payment_terms_template: function() {
|
||||
cur_frm.trigger("disable_due_date");
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Payment Schedule", {
|
||||
payment_schedule_remove: function() {
|
||||
cur_frm.trigger("disable_due_date");
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
frappe.ui.form.on('Payment Entry', {
|
||||
mode_of_payment: function(frm) {
|
||||
|
@ -102,6 +102,10 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
});
|
||||
}
|
||||
|
||||
if(this.frm.fields_dict["payment_terms_template"]){
|
||||
this.frm.trigger("payment_terms_template");
|
||||
}
|
||||
|
||||
if(this.frm.fields_dict["taxes"]) {
|
||||
this["taxes_remove"] = this.calculate_taxes_and_totals;
|
||||
}
|
||||
@ -1155,6 +1159,46 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
payment_terms_template: function() {
|
||||
var me = this;
|
||||
if(this.frm.doc.payment_terms_template && this.frm.doc.payment_schedule.length === 0) {
|
||||
frappe.call({
|
||||
method: "erpnext.controllers.accounts_controller.get_payment_terms",
|
||||
args: {
|
||||
terms_template: this.frm.doc.payment_terms_template,
|
||||
posting_date: this.frm.doc.posting_date || this.frm.doc.transaction_date,
|
||||
grand_total: this.frm.doc.rounded_total || this.frm.doc.grand_total
|
||||
},
|
||||
callback: function(r) {
|
||||
if(r.message && !r.exc) {
|
||||
me.frm.set_value("payment_schedule", r.message);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
payment_term: function(doc, cdt, cdn) {
|
||||
var row = locals[cdt][cdn];
|
||||
if(row.payment_term) {
|
||||
frappe.call({
|
||||
method: "erpnext.controllers.accounts_controller.get_payment_term_details",
|
||||
args: {
|
||||
term: row.payment_term,
|
||||
posting_date: this.frm.doc.posting_date || this.frm.doc.transaction_date,
|
||||
grand_total: this.frm.doc.rounded_total || this.frm.doc.grand_total
|
||||
},
|
||||
callback: function(r) {
|
||||
if(r.message && !r.exc) {
|
||||
for (var d in r.message) {
|
||||
frappe.model.set_value(cdt, cdn, d, r.message[d]);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
erpnext.show_serial_batch_selector = function(frm, d, callback, show_dialog) {
|
||||
|
@ -786,7 +786,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "eval:doc.credit_days || doc.credit_limit",
|
||||
"collapsible_depends_on": "",
|
||||
"columns": 0,
|
||||
"fieldname": "credit_limit_section",
|
||||
"fieldtype": "Section Break",
|
||||
@ -812,69 +812,6 @@
|
||||
"unique": 0,
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "credit_days_based_on",
|
||||
"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": "Credit Days Based On",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "\nFixed Days\nLast Day of the Next Month",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.credit_days_based_on=='Fixed Days'",
|
||||
"fieldname": "credit_days",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Credit Days",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "credit_days",
|
||||
"oldfieldtype": "Int",
|
||||
"permlevel": 1,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@ -907,6 +844,38 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "",
|
||||
"fieldname": "payment_terms",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Default Payment Terms Template",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Payment Terms Template",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@ -1202,8 +1171,8 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-07-24 00:55:07.445783",
|
||||
"modified_by": "Administrator",
|
||||
"modified": "2017-08-31 15:12:18.637132",
|
||||
"modified_by": "tundebabzy@gmail.com",
|
||||
"module": "Selling",
|
||||
"name": "Customer",
|
||||
"name_case": "Title Case",
|
||||
|
23
erpnext/selling/doctype/customer/test_customer.js
Normal file
23
erpnext/selling/doctype/customer/test_customer.js
Normal file
@ -0,0 +1,23 @@
|
||||
/* eslint-disable */
|
||||
// rename this file from _test_[name] to test_[name] to activate
|
||||
// and remove above this line
|
||||
|
||||
QUnit.test("test: Customer", function (assert) {
|
||||
let done = assert.async();
|
||||
|
||||
// number of asserts
|
||||
assert.expect(1);
|
||||
|
||||
frappe.run_serially([
|
||||
// insert a new Customer
|
||||
() => frappe.tests.make('Customer', [
|
||||
// values to be set
|
||||
{key: 'value'}
|
||||
]),
|
||||
() => {
|
||||
assert.equal(cur_frm.doc.key, 'value');
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
|
||||
});
|
@ -6,6 +6,7 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
from erpnext.accounts.party import get_due_date
|
||||
from frappe.test_runner import make_test_records
|
||||
from erpnext.exceptions import PartyFrozen, PartyDisabled
|
||||
from frappe.utils import flt
|
||||
@ -13,7 +14,7 @@ from erpnext.selling.doctype.customer.customer import get_credit_limit, get_cust
|
||||
from erpnext.tests.utils import create_test_contact_and_address
|
||||
|
||||
test_ignore = ["Price List"]
|
||||
|
||||
test_dependencies = ['Payment Term', 'Payment Terms Template']
|
||||
test_records = frappe.get_test_records('Customer')
|
||||
|
||||
class TestCustomer(unittest.TestCase):
|
||||
@ -181,6 +182,35 @@ class TestCustomer(unittest.TestCase):
|
||||
customer.credit_limit = flt(outstanding_amt - 100)
|
||||
self.assertRaises(frappe.ValidationError, customer.save)
|
||||
|
||||
def test_customer_payment_terms(self):
|
||||
frappe.db.set_value(
|
||||
"Customer", "_Test Customer With Template", "payment_terms", "_Test Payment Term Template 3")
|
||||
|
||||
due_date = get_due_date("2016-01-22", "Customer", "_Test Customer With Template")
|
||||
self.assertEqual(due_date, "2016-02-21")
|
||||
|
||||
due_date = get_due_date("2017-01-22", "Customer", "_Test Customer With Template")
|
||||
self.assertEqual(due_date, "2017-02-21")
|
||||
|
||||
frappe.db.set_value(
|
||||
"Customer", "_Test Customer With Template", "payment_terms", "_Test Payment Term Template 1")
|
||||
|
||||
due_date = get_due_date("2016-01-22", "Customer", "_Test Customer With Template")
|
||||
self.assertEqual(due_date, "2016-02-29")
|
||||
|
||||
due_date = get_due_date("2017-01-22", "Customer", "_Test Customer With Template")
|
||||
self.assertEqual(due_date, "2017-02-28")
|
||||
|
||||
frappe.db.set_value("Customer", "_Test Customer With Template", "payment_terms", "")
|
||||
|
||||
# No default payment term template attached
|
||||
due_date = get_due_date("2016-01-22", "Customer", "_Test Customer")
|
||||
self.assertEqual(due_date, "2016-01-22")
|
||||
|
||||
due_date = get_due_date("2017-01-22", "Customer", "_Test Customer")
|
||||
self.assertEqual(due_date, "2017-01-22")
|
||||
|
||||
|
||||
def get_customer_dict(customer_name):
|
||||
return {
|
||||
"customer_group": "_Test Customer Group",
|
||||
|
@ -1,4 +1,18 @@
|
||||
[
|
||||
{
|
||||
"customer_group": "_Test Customer Group",
|
||||
"customer_name": "_Test Customer With Template",
|
||||
"customer_type": "Individual",
|
||||
"doctype": "Customer",
|
||||
"territory": "_Test Territory"
|
||||
},
|
||||
{
|
||||
"customer_group": "_Test Customer Group",
|
||||
"customer_name": "_Test Customer P",
|
||||
"customer_type": "Individual",
|
||||
"doctype": "Customer",
|
||||
"territory": "_Test Territory"
|
||||
},
|
||||
{
|
||||
"customer_group": "_Test Customer Group",
|
||||
"customer_name": "_Test Customer",
|
||||
|
@ -2152,6 +2152,100 @@
|
||||
"unique": 0,
|
||||
"width": "200px"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"collapsible_depends_on": "",
|
||||
"columns": 0,
|
||||
"depends_on": "",
|
||||
"fieldname": "payment_schedule_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Terms",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "payment_terms_template",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Terms Template",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Payment Terms Template",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "payment_schedule",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Schedule",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Payment Schedule",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@ -2757,7 +2851,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 1,
|
||||
"menu_index": 0,
|
||||
"modified": "2017-11-15 01:01:16.774645",
|
||||
"modified": "2017-11-17 01:01:16.774645",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Quotation",
|
||||
|
@ -8,7 +8,29 @@ import unittest
|
||||
|
||||
test_dependencies = ["Product Bundle"]
|
||||
|
||||
|
||||
class TestQuotation(unittest.TestCase):
|
||||
def test_make_quotation_without_terms(self):
|
||||
quotation = make_quotation(do_not_save=1)
|
||||
self.assertFalse(quotation.get('payment_schedule'))
|
||||
|
||||
quotation.insert()
|
||||
|
||||
self.assertTrue(quotation.payment_schedule)
|
||||
|
||||
def test_make_sales_order_terms_not_copied(self):
|
||||
from erpnext.selling.doctype.quotation.quotation import make_sales_order
|
||||
|
||||
quotation = frappe.copy_doc(test_records[0])
|
||||
quotation.transaction_date = nowdate()
|
||||
quotation.valid_till = add_months(quotation.transaction_date, 1)
|
||||
quotation.insert()
|
||||
quotation.submit()
|
||||
|
||||
sales_order = make_sales_order(quotation.name)
|
||||
|
||||
self.assertFalse(sales_order.get('payment_schedule'))
|
||||
|
||||
def test_make_sales_order(self):
|
||||
from erpnext.selling.doctype.quotation.quotation import make_sales_order
|
||||
|
||||
@ -33,6 +55,46 @@ class TestQuotation(unittest.TestCase):
|
||||
sales_order.transaction_date = nowdate()
|
||||
sales_order.insert()
|
||||
|
||||
def test_make_sales_order_with_terms(self):
|
||||
from erpnext.selling.doctype.quotation.quotation import make_sales_order
|
||||
|
||||
quotation = frappe.copy_doc(test_records[0])
|
||||
quotation.transaction_date = nowdate()
|
||||
quotation.valid_till = add_months(quotation.transaction_date, 1)
|
||||
quotation.update(
|
||||
{"payment_terms_template": "_Test Payment Term Template"}
|
||||
)
|
||||
quotation.insert()
|
||||
|
||||
self.assertRaises(frappe.ValidationError, make_sales_order, quotation.name)
|
||||
quotation.save()
|
||||
quotation.submit()
|
||||
|
||||
self.assertEqual(quotation.payment_schedule[0].payment_amount, 8906.25)
|
||||
self.assertEqual(quotation.payment_schedule[0].due_date, quotation.transaction_date)
|
||||
self.assertEqual(quotation.payment_schedule[1].payment_amount, 8906.25)
|
||||
self.assertEqual(quotation.payment_schedule[1].due_date, add_days(quotation.transaction_date, 30))
|
||||
|
||||
sales_order = make_sales_order(quotation.name)
|
||||
|
||||
self.assertEquals(sales_order.doctype, "Sales Order")
|
||||
self.assertEquals(len(sales_order.get("items")), 1)
|
||||
self.assertEquals(sales_order.get("items")[0].doctype, "Sales Order Item")
|
||||
self.assertEquals(sales_order.get("items")[0].prevdoc_docname, quotation.name)
|
||||
self.assertEquals(sales_order.customer, "_Test Customer")
|
||||
|
||||
sales_order.delivery_date = "2014-01-01"
|
||||
sales_order.naming_series = "_T-Quotation-"
|
||||
sales_order.transaction_date = nowdate()
|
||||
sales_order.insert()
|
||||
|
||||
self.assertEqual(sales_order.payment_schedule[0].payment_amount, 8906.25)
|
||||
self.assertEqual(sales_order.payment_schedule[0].due_date, quotation.transaction_date)
|
||||
self.assertEqual(sales_order.payment_schedule[1].payment_amount, 8906.25)
|
||||
self.assertEqual(
|
||||
sales_order.payment_schedule[1].due_date, add_days(quotation.transaction_date, 30)
|
||||
)
|
||||
|
||||
def test_valid_till(self):
|
||||
from erpnext.selling.doctype.quotation.quotation import make_sales_order
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
QUnit.test("test: quotation", function (assert) {
|
||||
assert.expect(10);
|
||||
assert.expect(12);
|
||||
let done = assert.async();
|
||||
frappe.run_serially([
|
||||
() => {
|
||||
@ -10,7 +10,8 @@ QUnit.test("test: quotation", function (assert) {
|
||||
{"item_code": "Test Product 1"},
|
||||
{"qty": 5}
|
||||
]]
|
||||
}
|
||||
},
|
||||
{payment_terms_template: '_Test Payment Term Template UI'}
|
||||
]);
|
||||
},
|
||||
() => {
|
||||
@ -18,7 +19,7 @@ QUnit.test("test: quotation", function (assert) {
|
||||
assert.ok(cur_frm.doc.items[0].item_name == "Test Product 1", "Added Test Product 1");
|
||||
|
||||
// calculate_taxes_and_totals
|
||||
assert.ok(cur_frm.doc.grand_total === 500, "Total Amount is correct");
|
||||
assert.ok(cur_frm.doc.grand_total === 500, String(cur_frm.doc.grand_total));
|
||||
},
|
||||
() => cur_frm.set_value("customer_address", "Test1-Billing"),
|
||||
() => cur_frm.set_value("shipping_address_name", "Test1-Warehouse"),
|
||||
@ -30,6 +31,7 @@ QUnit.test("test: quotation", function (assert) {
|
||||
() => cur_frm.doc.items[0].rate = 200,
|
||||
() => frappe.timeout(0.3),
|
||||
() => cur_frm.set_value("tc_name", "Test Term 1"),
|
||||
() => cur_frm.set_value("payment_schedule", []),
|
||||
() => frappe.timeout(0.5),
|
||||
() => cur_frm.save(),
|
||||
() => {
|
||||
@ -47,6 +49,9 @@ QUnit.test("test: quotation", function (assert) {
|
||||
// Check Terms and Condtions
|
||||
assert.ok(cur_frm.doc.tc_name == "Test Term 1", "Terms and Conditions Checked");
|
||||
|
||||
assert.ok(cur_frm.doc.payment_terms_template, "Payment Terms Template is correct");
|
||||
assert.ok(cur_frm.doc.payment_schedule.length > 0, "Payment Term Schedule is not empty");
|
||||
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
|
@ -16,13 +16,15 @@ QUnit.test("test quotation with additional discount in grand total", function(as
|
||||
]},
|
||||
{customer_address: 'Test1-Billing'},
|
||||
{shipping_address_name: 'Test1-Shipping'},
|
||||
{contact_person: 'Contact 1-Test Customer 1'}
|
||||
{contact_person: 'Contact 1-Test Customer 1'},
|
||||
{payment_terms_template: '_Test Payment Term Template UI'}
|
||||
]);
|
||||
},
|
||||
() => {
|
||||
return frappe.tests.set_form_values(cur_frm, [
|
||||
{apply_discount_on:'Grand Total'},
|
||||
{additional_discount_percentage:10}
|
||||
{additional_discount_percentage:10},
|
||||
{payment_schedule: []}
|
||||
]);
|
||||
},
|
||||
() => cur_frm.save(),
|
||||
|
@ -2319,10 +2319,9 @@
|
||||
"label": "Packed Items",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "packing_details",
|
||||
"oldfieldtype": "Table",
|
||||
"options": "Packed Item",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
@ -2333,6 +2332,99 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"collapsible_depends_on": "",
|
||||
"columns": 0,
|
||||
"fieldname": "payment_schedule_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Terms",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "payment_terms_template",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Terms Template",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Payment Terms Template",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "payment_schedule",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payment Schedule",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Payment Schedule",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@ -3407,9 +3499,9 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-11-15 01:02:08.674118",
|
||||
"modified": "2017-11-17 01:02:08.674118",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"module": "Selling",
|
||||
"name": "Sales Order",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
|
23
erpnext/selling/doctype/sales_order/test_sales_order.js
Normal file
23
erpnext/selling/doctype/sales_order/test_sales_order.js
Normal file
@ -0,0 +1,23 @@
|
||||
/* eslint-disable */
|
||||
// rename this file from _test_[name] to test_[name] to activate
|
||||
// and remove above this line
|
||||
|
||||
QUnit.test("test: Sales Order", function (assert) {
|
||||
let done = assert.async();
|
||||
|
||||
// number of asserts
|
||||
assert.expect(1);
|
||||
|
||||
frappe.run_serially('Sales Order', [
|
||||
// insert a new Sales Order
|
||||
() => frappe.tests.make([
|
||||
// values to be set
|
||||
{key: 'value'}
|
||||
]),
|
||||
() => {
|
||||
assert.equal(cur_frm.doc.key, 'value');
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
|
||||
});
|
@ -13,6 +13,7 @@ from frappe.tests.test_permissions import set_user_permission_doctypes
|
||||
from erpnext.selling.doctype.sales_order.sales_order import make_production_orders
|
||||
import json
|
||||
|
||||
|
||||
class TestSalesOrder(unittest.TestCase):
|
||||
def tearDown(self):
|
||||
frappe.set_user("Administrator")
|
||||
@ -60,6 +61,32 @@ class TestSalesOrder(unittest.TestCase):
|
||||
si1 = make_sales_invoice(so.name)
|
||||
self.assertEquals(len(si1.get("items")), 0)
|
||||
|
||||
def test_make_sales_invoice_with_terms(self):
|
||||
so = make_sales_order(do_not_submit=True)
|
||||
|
||||
self.assertRaises(frappe.ValidationError, make_sales_invoice, so.name)
|
||||
|
||||
so.update({"payment_terms_template": "_Test Payment Term Template"})
|
||||
|
||||
so.save()
|
||||
so.submit()
|
||||
si = make_sales_invoice(so.name)
|
||||
|
||||
self.assertEquals(len(si.get("items")), len(so.get("items")))
|
||||
self.assertEquals(len(si.get("items")), 1)
|
||||
|
||||
si.insert()
|
||||
|
||||
self.assertEqual(si.payment_schedule[0].payment_amount, 500.0)
|
||||
self.assertEqual(si.payment_schedule[0].due_date, so.transaction_date)
|
||||
self.assertEqual(si.payment_schedule[1].payment_amount, 500.0)
|
||||
self.assertEqual(si.payment_schedule[1].due_date, add_days(so.transaction_date, 30))
|
||||
|
||||
si.submit()
|
||||
|
||||
si1 = make_sales_invoice(so.name)
|
||||
self.assertEquals(len(si1.get("items")), 0)
|
||||
|
||||
def test_update_qty(self):
|
||||
so = make_sales_order()
|
||||
|
||||
@ -125,7 +152,6 @@ class TestSalesOrder(unittest.TestCase):
|
||||
so = make_sales_order()
|
||||
self.assertEqual(get_reserved_qty(), existing_reserved_qty + 10)
|
||||
|
||||
|
||||
dn = create_dn_against_so(so.name, 15)
|
||||
self.assertEqual(get_reserved_qty(), existing_reserved_qty)
|
||||
|
||||
@ -181,7 +207,6 @@ class TestSalesOrder(unittest.TestCase):
|
||||
make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100)
|
||||
make_stock_entry(item="_Test Item Home Desktop 100", target="_Test Warehouse - _TC", qty=10, rate=100)
|
||||
|
||||
|
||||
existing_reserved_qty_item1 = get_reserved_qty("_Test Item")
|
||||
existing_reserved_qty_item2 = get_reserved_qty("_Test Item Home Desktop 100")
|
||||
|
||||
@ -503,10 +528,39 @@ class TestSalesOrder(unittest.TestCase):
|
||||
|
||||
self.assertEquals(new_so.get("items")[0].rate, flt((price_list_rate*25)/100 + price_list_rate))
|
||||
new_so.items[0].margin_rate_or_amount = 25
|
||||
new_so.payment_schedule = []
|
||||
new_so.save()
|
||||
new_so.submit()
|
||||
|
||||
self.assertEquals(new_so.get("items")[0].rate, flt((price_list_rate*25)/100 + price_list_rate))
|
||||
|
||||
def test_terms_auto_added(self):
|
||||
so = make_sales_order(do_not_save=1)
|
||||
|
||||
self.assertFalse(so.get('payment_schedule'))
|
||||
|
||||
so.insert()
|
||||
|
||||
self.assertTrue(so.get('payment_schedule'))
|
||||
|
||||
def test_terms_not_copied(self):
|
||||
so = make_sales_order()
|
||||
self.assertTrue(so.get('payment_schedule'))
|
||||
|
||||
si = make_sales_invoice(so.name)
|
||||
self.assertFalse(si.get('payment_schedule'))
|
||||
|
||||
def test_terms_copied(self):
|
||||
so = make_sales_order(do_not_copy=1, do_not_save=1)
|
||||
so.payment_terms_template = '_Test Payment Term Template'
|
||||
so.insert()
|
||||
so.submit()
|
||||
self.assertTrue(so.get('payment_schedule'))
|
||||
|
||||
si = make_sales_invoice(so.name)
|
||||
si.insert()
|
||||
self.assertTrue(si.get('payment_schedule'))
|
||||
|
||||
def test_make_production_order(self):
|
||||
# Make a new Sales Order
|
||||
so = make_sales_order(**{
|
||||
@ -575,6 +629,10 @@ def make_sales_order(**args):
|
||||
so.insert()
|
||||
if not args.do_not_submit:
|
||||
so.submit()
|
||||
else:
|
||||
so.payment_schedule = []
|
||||
else:
|
||||
so.payment_schedule = []
|
||||
|
||||
return so
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
QUnit.module('Sales Order');
|
||||
|
||||
QUnit.test("test sales order", function(assert) {
|
||||
assert.expect(10);
|
||||
assert.expect(12);
|
||||
let done = assert.async();
|
||||
frappe.run_serially([
|
||||
() => {
|
||||
@ -19,7 +19,8 @@ QUnit.test("test sales order", function(assert) {
|
||||
{contact_person: 'Contact 1-Test Customer 1'},
|
||||
{taxes_and_charges: 'TEST In State GST'},
|
||||
{tc_name: 'Test Term 1'},
|
||||
{terms: 'This is Test'}
|
||||
{terms: 'This is Test'},
|
||||
{payment_terms_template: '_Test Payment Term Template UI'}
|
||||
]);
|
||||
},
|
||||
() => {
|
||||
@ -28,7 +29,7 @@ QUnit.test("test sales order", function(assert) {
|
||||
{currency: 'USD'}
|
||||
]);
|
||||
},
|
||||
() => frappe.timeout(1),
|
||||
() => frappe.timeout(1.5),
|
||||
() => {
|
||||
// get_item_details
|
||||
assert.ok(cur_frm.doc.items[0].item_name=='Test Product 3', "Item name correct");
|
||||
@ -36,18 +37,24 @@ QUnit.test("test sales order", function(assert) {
|
||||
assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST', "Tax details correct");
|
||||
// get tax account head details
|
||||
assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct");
|
||||
// calculate totals
|
||||
assert.ok(cur_frm.doc.items[0].price_list_rate==250, "Item 1 price_list_rate");
|
||||
assert.ok(cur_frm.doc.net_total== 1280.75, "net total correct ");
|
||||
assert.ok(cur_frm.doc.base_grand_total== flt(1511.29* cur_frm.doc.conversion_rate, precision('base_grand_total')), "base round total correct ");
|
||||
assert.ok(cur_frm.doc.grand_total== 1511.29 , "grand total correct ");
|
||||
assert.ok(cur_frm.doc.rounded_total== 1511.30, "rounded total correct ");
|
||||
},
|
||||
() => cur_frm.save(),
|
||||
() => frappe.timeout(1),
|
||||
() => cur_frm.print_doc(),
|
||||
() => frappe.timeout(1),
|
||||
() => {
|
||||
// Payment Terms
|
||||
assert.ok(cur_frm.doc.payment_terms_template, "Payment Terms Template is correct");
|
||||
assert.ok(cur_frm.doc.payment_schedule.length > 0, "Payment Term Schedule is not empty");
|
||||
|
||||
// totals
|
||||
assert.ok(cur_frm.doc.items[0].price_list_rate==250, "Item 1 price_list_rate");
|
||||
assert.ok(cur_frm.doc.net_total== 1280.75, "net total correct ");
|
||||
assert.ok(cur_frm.doc.base_grand_total== flt(1511.29* cur_frm.doc.conversion_rate, precision('base_grand_total')), String(flt(1511.29* cur_frm.doc.conversion_rate, precision('base_grand_total')) + ' ' + cur_frm.doc.base_grand_total));
|
||||
assert.ok(cur_frm.doc.grand_total== 1511.29 , "grand total correct ");
|
||||
assert.ok(cur_frm.doc.rounded_total== 1511.30, "rounded total correct ");
|
||||
|
||||
// print format
|
||||
assert.ok($('.btn-print-print').is(':visible'), "Print Format Available");
|
||||
frappe.timeout(1);
|
||||
assert.ok($(".section-break+ .section-break .column-break:nth-child(1) .data-field:nth-child(1) .value").text().includes("Billing Street 1"), "Print Preview Works As Expected");
|
||||
|
@ -16,13 +16,15 @@ QUnit.test("test sales order with additional discount in grand total", function(
|
||||
]},
|
||||
{customer_address: 'Test1-Billing'},
|
||||
{shipping_address_name: 'Test1-Shipping'},
|
||||
{contact_person: 'Contact 1-Test Customer 1'}
|
||||
{contact_person: 'Contact 1-Test Customer 1'},
|
||||
{payment_terms_template: '_Test Payment Term Template UI'}
|
||||
]);
|
||||
},
|
||||
() => {
|
||||
return frappe.tests.set_form_values(cur_frm, [
|
||||
{apply_discount_on:'Grand Total'},
|
||||
{additional_discount_percentage:10}
|
||||
{additional_discount_percentage:10},
|
||||
{payment_schedule: []}
|
||||
]);
|
||||
},
|
||||
() => cur_frm.save(),
|
||||
|
@ -18,7 +18,8 @@ QUnit.test("test sales order", function(assert) {
|
||||
]},
|
||||
{customer_address: 'Test1-Billing'},
|
||||
{shipping_address_name: 'Test1-Shipping'},
|
||||
{contact_person: 'Contact 1-Test Customer 1'}
|
||||
{contact_person: 'Contact 1-Test Customer 1'},
|
||||
{payment_terms_template: '_Test Payment Term Template UI'}
|
||||
]);
|
||||
},
|
||||
() => cur_frm.save(),
|
||||
|
@ -4,7 +4,7 @@ QUnit.test("test:Point of Sales", function(assert) {
|
||||
|
||||
frappe.run_serially([
|
||||
() => frappe.set_route('point-of-sale'),
|
||||
() => frappe.timeout(2),
|
||||
() => frappe.timeout(3),
|
||||
() => frappe.set_control('customer', 'Test Customer 1'),
|
||||
() => frappe.timeout(0.2),
|
||||
() => cur_frm.set_value('customer', 'Test Customer 1'),
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:supplier_type",
|
||||
@ -12,6 +13,7 @@
|
||||
"editable_grid": 0,
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -42,6 +44,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
@ -71,12 +74,13 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "credit_days_based_on",
|
||||
"fieldtype": "Select",
|
||||
"fieldname": "payment_terms",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
@ -84,39 +88,10 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Credit Days Based On",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "\nFixed Days\nLast Day of the Next Month",
|
||||
"permlevel": 1,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.credit_days_based_on=='Fixed Days'",
|
||||
"fieldname": "credit_days",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Credit Days",
|
||||
"label": "Default Payment Terms Template",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Payment Terms Template",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
@ -130,6 +105,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -158,6 +134,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@ -189,18 +166,18 @@
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "fa fa-flag",
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-02-20 13:25:25.641431",
|
||||
"modified": "2017-09-04 18:54:10.093500",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Supplier Type",
|
||||
|
23
erpnext/setup/doctype/supplier_type/test_supplier_type.js
Normal file
23
erpnext/setup/doctype/supplier_type/test_supplier_type.js
Normal file
@ -0,0 +1,23 @@
|
||||
/* eslint-disable */
|
||||
// rename this file from _test_[name] to test_[name] to activate
|
||||
// and remove above this line
|
||||
|
||||
QUnit.test("test: Supplier Type", function (assert) {
|
||||
let done = assert.async();
|
||||
|
||||
// number of asserts
|
||||
assert.expect(1);
|
||||
|
||||
frappe.run_serially([
|
||||
// insert a new Supplier Type
|
||||
() => frappe.tests.make('Supplier Type', [
|
||||
// values to be set
|
||||
{key: 'value'}
|
||||
]),
|
||||
() => {
|
||||
assert.equal(cur_frm.doc.key, 'value');
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
|
||||
});
|
@ -12,7 +12,9 @@ from frappe.utils.nestedset import get_root_of
|
||||
from erpnext.accounts.utils import get_account_name
|
||||
from erpnext.utilities.product import get_qty_in_stock
|
||||
|
||||
class WebsitePriceListMissingError(frappe.ValidationError): pass
|
||||
|
||||
class WebsitePriceListMissingError(frappe.ValidationError):
|
||||
pass
|
||||
|
||||
def set_cart_count(quotation=None):
|
||||
if cint(frappe.db.get_singles_value("Shopping Cart Settings", "enabled")):
|
||||
@ -104,6 +106,7 @@ def update_cart(item_code, qty, with_items=False):
|
||||
apply_cart_settings(quotation=quotation)
|
||||
|
||||
quotation.flags.ignore_permissions = True
|
||||
quotation.payment_schedule = []
|
||||
if not empty_card:
|
||||
quotation.save()
|
||||
else:
|
||||
@ -179,6 +182,7 @@ def decorate_quotation_doc(doc):
|
||||
|
||||
return doc
|
||||
|
||||
|
||||
def _get_cart_quotation(party=None):
|
||||
'''Return the open Quotation of type "Shopping Cart" or make a new one'''
|
||||
if not party:
|
||||
@ -200,6 +204,7 @@ def _get_cart_quotation(party=None):
|
||||
"status": "Draft",
|
||||
"docstatus": 0,
|
||||
"__islocal": 1,
|
||||
"payment_terms_template": "_Test Payment Term Template",
|
||||
(party.doctype.lower()): party.name
|
||||
})
|
||||
|
||||
|
@ -8,6 +8,9 @@ from frappe.utils import nowdate, add_months
|
||||
from erpnext.shopping_cart.cart import _get_cart_quotation, update_cart, get_party
|
||||
from erpnext.tests.utils import create_test_contact_and_address
|
||||
|
||||
|
||||
test_dependencies = ['Payment Terms Template']
|
||||
|
||||
class TestShoppingCart(unittest.TestCase):
|
||||
"""
|
||||
Note:
|
||||
@ -62,7 +65,6 @@ class TestShoppingCart(unittest.TestCase):
|
||||
self.assertEquals(quotation.get("items")[0].qty, 1)
|
||||
self.assertEquals(quotation.get("items")[0].amount, 10)
|
||||
|
||||
|
||||
# add second item
|
||||
update_cart("_Test Item 2", 1)
|
||||
quotation = self.test_get_cart_customer()
|
||||
|
@ -122,7 +122,8 @@ def create_material_request(material_requests):
|
||||
mr.update({
|
||||
"company": company,
|
||||
"transaction_date": nowdate(),
|
||||
"material_request_type": "Material Transfer" if request_type=="Transfer" else request_type
|
||||
"material_request_type": "Material Transfer" if request_type=="Transfer" else request_type,
|
||||
"schedule_date": add_days(nowdate(), cint(items[0].lead_time_days))
|
||||
})
|
||||
|
||||
for d in items:
|
||||
|
@ -217,6 +217,25 @@ $.extend(frappe.test_data, {
|
||||
{price_list: '_Test Price List'},
|
||||
{price_list_rate: 200}
|
||||
]
|
||||
},
|
||||
"Payment Term": {
|
||||
"_Test Payment Term": [
|
||||
{payment_term_name: '_Test Payment Term'},
|
||||
{due_date_based_on: 'Day(s) after invoice date'},
|
||||
{invoice_portion: 100},
|
||||
{credit_days: 0}
|
||||
]
|
||||
},
|
||||
"Payment Terms Template": {
|
||||
"_Test Payment Term Template UI": [
|
||||
{template_name: "_Test Payment Term Template UI"},
|
||||
{terms: [
|
||||
[
|
||||
{payment_term: '_Test Payment Term'},
|
||||
{invoice_portion: 100}
|
||||
]
|
||||
]}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user