Merge pull request #3848 from rmehta/journal-entry-refactor
[refactor] added dynamic link in journal entry, #3847, #3814
This commit is contained in:
commit
57ca765d9e
@ -19,7 +19,8 @@ class BankReconciliation(Document):
|
||||
|
||||
|
||||
dl = frappe.db.sql("""select t1.name, t1.cheque_no, t1.cheque_date, t2.debit,
|
||||
t2.credit, t1.posting_date, t2.against_account, t1.clearance_date
|
||||
t2.credit, t1.posting_date, t2.against_account, t1.clearance_date,
|
||||
t2.reference_type, t2.reference_name
|
||||
from
|
||||
`tabJournal Entry` t1, `tabJournal Entry Account` t2
|
||||
where
|
||||
|
@ -1,11 +1,19 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"creation": "2013-02-22 01:27:37",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "voucher_id",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Voucher ID",
|
||||
"no_copy": 0,
|
||||
@ -13,46 +21,84 @@
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Journal Entry",
|
||||
"permlevel": 0,
|
||||
"search_index": 0
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "clearance_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Clearance Date",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "clearance_date",
|
||||
"oldfieldtype": "Date",
|
||||
"permlevel": 0,
|
||||
"search_index": 0
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "against_account",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Against Account",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "against_account",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"search_index": 0
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "cheque_number",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Cheque Number",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "cheque_number",
|
||||
"oldfieldtype": "Data",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"search_index": 0
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "debit",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Debit",
|
||||
"no_copy": 0,
|
||||
@ -60,12 +106,21 @@
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"search_index": 0
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "credit",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Credit",
|
||||
"no_copy": 0,
|
||||
@ -73,40 +128,113 @@
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"search_index": 0
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "reference_type",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Reference Type",
|
||||
"no_copy": 0,
|
||||
"options": "DocType",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "reference_name",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Reference Name",
|
||||
"no_copy": 0,
|
||||
"options": "reference_type",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "posting_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Posting Date",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "posting_date",
|
||||
"oldfieldtype": "Date",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"search_index": 0
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "cheque_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Cheque Date",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "cheque_date",
|
||||
"oldfieldtype": "Date",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"search_index": 0
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 1,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-04-21 01:29:29.570890",
|
||||
"modified": "2015-08-10 16:59:43.974705",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Bank Reconciliation Detail",
|
||||
"owner": "Administrator",
|
||||
"permissions": []
|
||||
"permissions": [],
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0
|
||||
}
|
@ -50,22 +50,18 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
||||
|
||||
$.each([["against_voucher", "Purchase Invoice", "supplier"],
|
||||
["against_invoice", "Sales Invoice", "customer"]], function(i, opts) {
|
||||
me.frm.set_query(opts[0], "accounts", function(doc, cdt, cdn) {
|
||||
var jvd = frappe.get_doc(cdt, cdn);
|
||||
frappe.model.validate_missing(jvd, "party_type");
|
||||
frappe.model.validate_missing(jvd, "party");
|
||||
return {
|
||||
filters: [
|
||||
[opts[1], opts[2], "=", jvd.party],
|
||||
[opts[1], "docstatus", "=", 1],
|
||||
[opts[1], "outstanding_amount", "!=", 0]
|
||||
]
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
this.frm.set_query("against_jv", "accounts", function(doc, cdt, cdn) {
|
||||
me.frm.set_query("reference_name", "accounts", function(doc, cdt, cdn) {
|
||||
var jvd = frappe.get_doc(cdt, cdn);
|
||||
|
||||
// expense claim
|
||||
if(jvd.reference_type==="Expense Claim") {
|
||||
return {};
|
||||
}
|
||||
|
||||
// journal entry
|
||||
if(jvd.reference_type==="Journal Entry") {
|
||||
frappe.model.validate_missing(jvd, "account");
|
||||
|
||||
return {
|
||||
@ -75,7 +71,30 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
||||
party: jvd.party
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// against party
|
||||
|
||||
frappe.model.validate_missing(jvd, "party_type");
|
||||
frappe.model.validate_missing(jvd, "party");
|
||||
|
||||
var out = {
|
||||
filters: [
|
||||
[jvd.reference_type, jvd.reference_type.indexOf("Sales")===0 ? "customer" : "supplier", "=", jvd.party],
|
||||
[jvd.reference_type, "docstatus", "=", 1],
|
||||
]
|
||||
};
|
||||
|
||||
if(in_list(["Sales Invoice", "Purchase Invoice"], jvd.reference_type)) {
|
||||
out.filters.push([jvd.reference_type, "outstanding_amount", "!=", 0]);
|
||||
} else {
|
||||
out.filters.push([jvd.reference_type, "per_billed", "<", 100]);
|
||||
}
|
||||
|
||||
return out;
|
||||
});
|
||||
|
||||
|
||||
},
|
||||
|
||||
setup_balance_formatter: function() {
|
||||
@ -93,24 +112,16 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
||||
})
|
||||
},
|
||||
|
||||
against_voucher: function(doc, cdt, cdn) {
|
||||
reference_name: function(doc, cdt, cdn) {
|
||||
var d = frappe.get_doc(cdt, cdn);
|
||||
if (d.against_voucher && !flt(d.debit)) {
|
||||
this.get_outstanding('Purchase Invoice', d.against_voucher, d);
|
||||
if (d.reference_type==="Purchase Invoice" && !flt(d.debit)) {
|
||||
this.get_outstanding('Purchase Invoice', d.reference_name, d);
|
||||
}
|
||||
},
|
||||
|
||||
against_invoice: function(doc, cdt, cdn) {
|
||||
var d = frappe.get_doc(cdt, cdn);
|
||||
if (d.against_invoice && !flt(d.credit)) {
|
||||
this.get_outstanding('Sales Invoice', d.against_invoice, d);
|
||||
if (d.reference_type==="Sales Invoice" && !flt(d.credit)) {
|
||||
this.get_outstanding('Sales Invoice', d.reference_name, d);
|
||||
}
|
||||
},
|
||||
|
||||
against_jv: function(doc, cdt, cdn) {
|
||||
var d = frappe.get_doc(cdt, cdn);
|
||||
if (d.against_jv && !flt(d.credit) && !flt(d.debit)) {
|
||||
this.get_outstanding('Journal Entry', d.against_jv, d);
|
||||
if (d.reference_type==="Journal Entry" && !flt(d.credit) && !flt(d.debit)) {
|
||||
this.get_outstanding('Journal Entry', d.reference_name, d);
|
||||
}
|
||||
},
|
||||
|
||||
@ -214,11 +225,12 @@ cur_frm.cscript.account = function(doc,dt,dn) {
|
||||
var d = locals[dt][dn];
|
||||
if(d.account) {
|
||||
return frappe.call({
|
||||
method: "erpnext.accounts.utils.get_balance_on",
|
||||
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_account_balance_and_party_type",
|
||||
args: {account: d.account, date: doc.posting_date},
|
||||
callback: function(r) {
|
||||
d.balance = r.message;
|
||||
$.extend(d, r.message);
|
||||
refresh_field('balance', d.name, 'accounts');
|
||||
refresh_field('party_type', d.name, 'accounts');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -28,13 +28,10 @@ class JournalEntry(AccountsController):
|
||||
self.validate_entries_for_advance()
|
||||
self.validate_debit_and_credit()
|
||||
self.validate_against_jv()
|
||||
self.validate_against_sales_invoice()
|
||||
self.validate_against_purchase_invoice()
|
||||
self.validate_reference_doc()
|
||||
self.set_against_account()
|
||||
self.create_remarks()
|
||||
self.set_print_format_fields()
|
||||
self.validate_against_sales_order()
|
||||
self.validate_against_purchase_order()
|
||||
self.check_due_date()
|
||||
self.validate_expense_claim()
|
||||
self.validate_credit_debit_note()
|
||||
@ -54,10 +51,8 @@ class JournalEntry(AccountsController):
|
||||
advance_paid = frappe._dict()
|
||||
for d in self.get("accounts"):
|
||||
if d.is_advance:
|
||||
if d.against_sales_order:
|
||||
advance_paid.setdefault("Sales Order", []).append(d.against_sales_order)
|
||||
elif d.against_purchase_order:
|
||||
advance_paid.setdefault("Purchase Order", []).append(d.against_purchase_order)
|
||||
if d.reference_type in ("Sales Order", "Purchase Order"):
|
||||
advance_paid.setdefault(d.reference_type, []).append(d.reference_name)
|
||||
|
||||
for voucher_type, order_list in advance_paid.items():
|
||||
for voucher_no in list(set(order_list)):
|
||||
@ -65,7 +60,7 @@ class JournalEntry(AccountsController):
|
||||
|
||||
def on_cancel(self):
|
||||
from erpnext.accounts.utils import remove_against_link_from_jv
|
||||
remove_against_link_from_jv(self.doctype, self.name, "against_jv")
|
||||
remove_against_link_from_jv(self.doctype, self.name)
|
||||
|
||||
self.make_gl_entries(1)
|
||||
self.update_advance_paid()
|
||||
@ -93,10 +88,8 @@ class JournalEntry(AccountsController):
|
||||
for d in self.get("accounts"):
|
||||
if d.party_type and d.party and d.get("credit" if d.party_type=="Customer" else "debit") > 0:
|
||||
due_date = None
|
||||
if d.against_invoice:
|
||||
due_date = frappe.db.get_value("Sales Invoice", d.against_invoice, "due_date")
|
||||
elif d.against_voucher:
|
||||
due_date = frappe.db.get_value("Purchase Invoice", d.against_voucher, "due_date")
|
||||
if d.reference_type in ("Sales Invoice", "Purchase Invoice"):
|
||||
due_date = frappe.db.get_value(d.reference_type, d.reference_name, "due_date")
|
||||
|
||||
if due_date and getdate(self.cheque_date) > getdate(due_date):
|
||||
diff = date_diff(self.cheque_date, due_date)
|
||||
@ -115,17 +108,17 @@ class JournalEntry(AccountsController):
|
||||
|
||||
def validate_entries_for_advance(self):
|
||||
for d in self.get('accounts'):
|
||||
if not (d.against_voucher and d.against_invoice and d.against_jv):
|
||||
if d.reference_type not in ("Sales Invoice", "Purchase Invoice", "Journal Entry"):
|
||||
if (d.party_type == 'Customer' and flt(d.credit) > 0) or \
|
||||
(d.party_type == 'Supplier' and flt(d.debit) > 0):
|
||||
if not d.is_advance:
|
||||
if d.is_advance=="No":
|
||||
msgprint(_("Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry.").format(d.idx, d.account))
|
||||
elif (d.against_sales_order or d.against_purchase_order) and d.is_advance != "Yes":
|
||||
elif d.reference_type in ("Sales Order", "Purchase Order") and d.is_advance != "Yes":
|
||||
frappe.throw(_("Row {0}: Payment against Sales/Purchase Order should always be marked as advance").format(d.idx))
|
||||
|
||||
def validate_against_jv(self):
|
||||
for d in self.get('accounts'):
|
||||
if d.against_jv:
|
||||
if d.reference_type=="Journal Entry":
|
||||
account_root_type = frappe.db.get_value("Account", d.account, "root_type")
|
||||
if account_root_type == "Asset" and flt(d.debit) > 0:
|
||||
frappe.throw(_("For {0}, only credit accounts can be linked against another debit entry")
|
||||
@ -134,17 +127,17 @@ class JournalEntry(AccountsController):
|
||||
frappe.throw(_("For {0}, only debit accounts can be linked against another credit entry")
|
||||
.format(d.account))
|
||||
|
||||
if d.against_jv == self.name:
|
||||
if d.reference_name == self.name:
|
||||
frappe.throw(_("You can not enter current voucher in 'Against Journal Entry' column"))
|
||||
|
||||
against_entries = frappe.db.sql("""select * from `tabJournal Entry Account`
|
||||
where account = %s and docstatus = 1 and parent = %s
|
||||
and ifnull(against_jv, '') = '' and ifnull(against_invoice, '') = ''
|
||||
and ifnull(against_voucher, '') = ''""", (d.account, d.against_jv), as_dict=True)
|
||||
and ifnull(reference_type, '') in ("", "Sales Order", "Purchase Order")
|
||||
""", (d.account, d.reference_name), as_dict=True)
|
||||
|
||||
if not against_entries:
|
||||
frappe.throw(_("Journal Entry {0} does not have account {1} or already matched against other voucher")
|
||||
.format(d.against_jv, d.account))
|
||||
.format(d.reference_name, d.account))
|
||||
else:
|
||||
dr_or_cr = "debit" if d.credit > 0 else "credit"
|
||||
valid = False
|
||||
@ -153,89 +146,99 @@ class JournalEntry(AccountsController):
|
||||
valid = True
|
||||
if not valid:
|
||||
frappe.throw(_("Against Journal Entry {0} does not have any unmatched {1} entry")
|
||||
.format(d.against_jv, dr_or_cr))
|
||||
.format(d.reference_name, dr_or_cr))
|
||||
|
||||
def validate_against_sales_invoice(self):
|
||||
self.validate_account_in_against_voucher("against_invoice", "Sales Invoice")
|
||||
|
||||
def validate_against_purchase_invoice(self):
|
||||
self.validate_account_in_against_voucher("against_voucher", "Purchase Invoice")
|
||||
|
||||
def validate_against_sales_order(self):
|
||||
payment_against_voucher = self.validate_account_in_against_voucher("against_sales_order", "Sales Order")
|
||||
self.validate_against_order_fields("Sales Order", payment_against_voucher)
|
||||
|
||||
def validate_against_purchase_order(self):
|
||||
payment_against_voucher = self.validate_account_in_against_voucher("against_purchase_order", "Purchase Order")
|
||||
self.validate_against_order_fields("Purchase Order", payment_against_voucher)
|
||||
|
||||
def validate_account_in_against_voucher(self, against_field, doctype):
|
||||
payment_against_voucher = frappe._dict()
|
||||
field_dict = {'Sales Invoice': ["Customer", "Debit To"],
|
||||
def validate_reference_doc(self):
|
||||
"""Validates reference document"""
|
||||
field_dict = {
|
||||
'Sales Invoice': ["Customer", "Debit To"],
|
||||
'Purchase Invoice': ["Supplier", "Credit To"],
|
||||
'Sales Order': ["Customer"],
|
||||
'Purchase Order': ["Supplier"]
|
||||
}
|
||||
|
||||
self.reference_totals = {}
|
||||
self.reference_types = {}
|
||||
|
||||
for d in self.get("accounts"):
|
||||
if d.get(against_field):
|
||||
dr_or_cr = "credit" if against_field in ["against_invoice", "against_sales_order"] \
|
||||
if not d.reference_type:
|
||||
d.reference_name = None
|
||||
if not d.reference_name:
|
||||
d.reference_type = None
|
||||
if d.reference_type and d.reference_name and (d.reference_type in field_dict.keys()):
|
||||
dr_or_cr = "credit" if d.reference_type in ("Sales Order", "Sales Invoice") \
|
||||
else "debit"
|
||||
if against_field == "against_sales_order" and flt(d.debit) > 0:
|
||||
frappe.throw(_("Row {0}: Debit entry can not be linked with a {1}").format(d.idx, doctype))
|
||||
|
||||
if against_field == "against_purchase_order" and flt(d.credit) > 0:
|
||||
frappe.throw(_("Row {0}: Credit entry can not be linked with a {1}").format(d.idx, doctype))
|
||||
# check debit or credit type Sales / Purchase Order
|
||||
if d.reference_type=="Sales Order" and flt(d.debit) > 0:
|
||||
frappe.throw(_("Row {0}: Debit entry can not be linked with a {1}").format(d.idx, d.reference_type))
|
||||
|
||||
against_voucher = frappe.db.get_value(doctype, d.get(against_field),
|
||||
[scrub(dt) for dt in field_dict.get(doctype)])
|
||||
if d.reference_type == "Purchase Order" and flt(d.credit) > 0:
|
||||
frappe.throw(_("Row {0}: Credit entry can not be linked with a {1}").format(d.idx, d.reference_type))
|
||||
|
||||
if against_field in ["against_invoice", "against_voucher"]:
|
||||
# set totals
|
||||
if not d.reference_name in self.reference_totals:
|
||||
self.reference_totals[d.reference_name] = 0.0
|
||||
self.reference_totals[d.reference_name] += flt(d.get(dr_or_cr))
|
||||
self.reference_types[d.reference_name] = d.reference_type
|
||||
|
||||
against_voucher = frappe.db.get_value(d.reference_type, d.reference_name,
|
||||
[scrub(dt) for dt in field_dict.get(d.reference_type)])
|
||||
|
||||
# check if party and account match
|
||||
if d.reference_type in ("Sales Invoice", "Purchase Invoice"):
|
||||
if (against_voucher[0] != d.party or against_voucher[1] != d.account):
|
||||
frappe.throw(_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}")
|
||||
.format(d.idx, field_dict.get(doctype)[0], field_dict.get(doctype)[1],
|
||||
doctype, d.get(against_field)))
|
||||
else:
|
||||
payment_against_voucher.setdefault(d.get(against_field), []).append(flt(d.get(dr_or_cr)))
|
||||
.format(d.idx, field_dict.get(d.reference_type)[0], field_dict.get(d.reference_type)[1],
|
||||
d.reference_type, d.reference_name))
|
||||
|
||||
if against_field in ["against_sales_order", "against_purchase_order"]:
|
||||
# check if party matches for Sales / Purchase Order
|
||||
if d.reference_type in ("Sales Order", "Purchase Order"):
|
||||
# set totals
|
||||
if against_voucher != d.party:
|
||||
frappe.throw(_("Row {0}: {1} {2} does not match with {3}") \
|
||||
.format(d.idx, d.party_type, d.party, doctype))
|
||||
elif d.is_advance == "Yes":
|
||||
payment_against_voucher.setdefault(d.get(against_field), []).append(flt(d.get(dr_or_cr)))
|
||||
.format(d.idx, d.party_type, d.party, d.reference_type))
|
||||
|
||||
return payment_against_voucher
|
||||
self.validate_orders()
|
||||
self.validate_invoices()
|
||||
|
||||
def validate_against_invoice_fields(self, doctype, payment_against_voucher):
|
||||
for voucher_no, payment_list in payment_against_voucher.items():
|
||||
voucher_properties = frappe.db.get_value(doctype, voucher_no,
|
||||
["docstatus", "outstanding_amount"])
|
||||
def validate_orders(self):
|
||||
"""Validate totals, stopped and docstatus for orders"""
|
||||
for reference_name, total in self.reference_totals.iteritems():
|
||||
reference_type = self.reference_types[reference_name]
|
||||
|
||||
if voucher_properties[0] != 1:
|
||||
frappe.throw(_("{0} {1} is not submitted").format(doctype, voucher_no))
|
||||
|
||||
if flt(voucher_properties[1]) < flt(sum(payment_list)):
|
||||
frappe.throw(_("Payment against {0} {1} cannot be greater \
|
||||
than Outstanding Amount {2}").format(doctype, voucher_no, voucher_properties[1]))
|
||||
|
||||
def validate_against_order_fields(self, doctype, payment_against_voucher):
|
||||
for voucher_no, payment_list in payment_against_voucher.items():
|
||||
voucher_properties = frappe.db.get_value(doctype, voucher_no,
|
||||
if reference_type in ("Sales Order", "Purchase Order"):
|
||||
voucher_properties = frappe.db.get_value(reference_type, reference_name,
|
||||
["docstatus", "per_billed", "status", "advance_paid", "base_grand_total"])
|
||||
|
||||
if voucher_properties[0] != 1:
|
||||
frappe.throw(_("{0} {1} is not submitted").format(doctype, voucher_no))
|
||||
frappe.throw(_("{0} {1} is not submitted").format(reference_type, reference_name))
|
||||
|
||||
if flt(voucher_properties[1]) >= 100:
|
||||
frappe.throw(_("{0} {1} is fully billed").format(doctype, voucher_no))
|
||||
frappe.throw(_("{0} {1} is fully billed").format(reference_type, reference_name))
|
||||
|
||||
if cstr(voucher_properties[2]) == "Stopped":
|
||||
frappe.throw(_("{0} {1} is stopped").format(doctype, voucher_no))
|
||||
frappe.throw(_("{0} {1} is stopped").format(reference_type, reference_name))
|
||||
|
||||
if flt(voucher_properties[4]) < flt(voucher_properties[3]) + flt(sum(payment_list)):
|
||||
if flt(voucher_properties[4]) < (flt(voucher_properties[3]) + total):
|
||||
frappe.throw(_("Advance paid against {0} {1} cannot be greater \
|
||||
than Grand Total {2}").format(doctype, voucher_no, voucher_properties[3]))
|
||||
than Grand Total {2}").format(reference_type, reference_name, voucher_properties[4]))
|
||||
|
||||
def validate_invoices(self):
|
||||
"""Validate totals and docstatus for invoices"""
|
||||
for reference_name, total in self.reference_totals.iteritems():
|
||||
reference_type = self.reference_types[reference_name]
|
||||
|
||||
if reference_type in ("Sales Invoice", "Purchase Invoice"):
|
||||
voucher_properties = frappe.db.get_value(reference_type, reference_name,
|
||||
["docstatus", "outstanding_amount"])
|
||||
|
||||
if voucher_properties[0] != 1:
|
||||
frappe.throw(_("{0} {1} is not submitted").format(reference_type, reference_name))
|
||||
|
||||
if flt(voucher_properties[1]) < total:
|
||||
frappe.throw(_("Payment against {0} {1} cannot be greater \
|
||||
than Outstanding Amount {2}").format(reference_type, reference_name, voucher_properties[1]))
|
||||
|
||||
def set_against_account(self):
|
||||
accounts_debited, accounts_credited = [], []
|
||||
@ -275,25 +278,25 @@ class JournalEntry(AccountsController):
|
||||
company_currency = get_company_currency(self.company)
|
||||
|
||||
for d in self.get('accounts'):
|
||||
if d.against_invoice and d.credit:
|
||||
if d.reference_type=="Sales Invoice" and d.credit:
|
||||
r.append(_("{0} against Sales Invoice {1}").format(fmt_money(flt(d.credit), currency = company_currency), \
|
||||
d.against_invoice))
|
||||
d.reference_name))
|
||||
|
||||
if d.against_sales_order and d.credit:
|
||||
if d.reference_type=="Sales Order" and d.credit:
|
||||
r.append(_("{0} against Sales Order {1}").format(fmt_money(flt(d.credit), currency = company_currency), \
|
||||
d.against_sales_order))
|
||||
d.reference_name))
|
||||
|
||||
if d.against_voucher and d.debit:
|
||||
if d.reference_type == "Purchase Invoice" and d.debit:
|
||||
bill_no = frappe.db.sql("""select bill_no, bill_date
|
||||
from `tabPurchase Invoice` where name=%s""", d.against_voucher)
|
||||
from `tabPurchase Invoice` where name=%s""", d.reference_name)
|
||||
if bill_no and bill_no[0][0] and bill_no[0][0].lower().strip() \
|
||||
not in ['na', 'not applicable', 'none']:
|
||||
r.append(_('{0} against Bill {1} dated {2}').format(fmt_money(flt(d.debit), currency=company_currency), bill_no[0][0],
|
||||
bill_no[0][1] and formatdate(bill_no[0][1].strftime('%Y-%m-%d'))))
|
||||
|
||||
if d.against_purchase_order and d.debit:
|
||||
if d.reference_type == "Purchase Order" and d.debit:
|
||||
r.append(_("{0} against Purchase Order {1}").format(fmt_money(flt(d.credit), currency = company_currency), \
|
||||
d.against_purchase_order))
|
||||
d.reference_name))
|
||||
|
||||
if self.user_remark:
|
||||
r.append(_("Note: {0}").format(self.user_remark))
|
||||
@ -332,13 +335,8 @@ class JournalEntry(AccountsController):
|
||||
"against": d.against_account,
|
||||
"debit": flt(d.debit, self.precision("debit", "accounts")),
|
||||
"credit": flt(d.credit, self.precision("credit", "accounts")),
|
||||
"against_voucher_type": (("Purchase Invoice" if d.against_voucher else None)
|
||||
or ("Sales Invoice" if d.against_invoice else None)
|
||||
or ("Journal Entry" if d.against_jv else None)
|
||||
or ("Sales Order" if d.against_sales_order else None)
|
||||
or ("Purchase Order" if d.against_purchase_order else None)),
|
||||
"against_voucher": d.against_voucher or d.against_invoice or d.against_jv
|
||||
or d.against_sales_order or d.against_purchase_order,
|
||||
"against_voucher_type": d.reference_type,
|
||||
"against_voucher": d.reference_name,
|
||||
"remarks": self.remark,
|
||||
"cost_center": d.cost_center
|
||||
})
|
||||
@ -385,11 +383,13 @@ class JournalEntry(AccountsController):
|
||||
if self.write_off_based_on == 'Accounts Receivable':
|
||||
jd1.party_type = "Customer"
|
||||
jd1.credit = flt(d.outstanding_amount, self.precision("credit", "accounts"))
|
||||
jd1.against_invoice = cstr(d.name)
|
||||
jd1.reference_type = "Sales Invoice"
|
||||
jd1.reference_name = cstr(d.name)
|
||||
elif self.write_off_based_on == 'Accounts Payable':
|
||||
jd1.party_type = "Supplier"
|
||||
jd1.debit = flt(d.outstanding_amount, self.precision("debit", "accounts"))
|
||||
jd1.against_voucher = cstr(d.name)
|
||||
jd1.reference_type = "Purchase Invoice"
|
||||
jd1.reference_name = cstr(d.name)
|
||||
|
||||
jd2 = self.append('accounts', {})
|
||||
if self.write_off_based_on == 'Accounts Receivable':
|
||||
@ -415,19 +415,20 @@ class JournalEntry(AccountsController):
|
||||
|
||||
def update_expense_claim(self):
|
||||
for d in self.accounts:
|
||||
if d.against_expense_claim:
|
||||
if d.reference_type=="Expense Claim":
|
||||
amt = frappe.db.sql("""select sum(debit) as amt from `tabJournal Entry Account`
|
||||
where against_expense_claim = %s and docstatus = 1""", d.against_expense_claim ,as_dict=1)[0].amt
|
||||
frappe.db.set_value("Expense Claim", d.against_expense_claim , "total_amount_reimbursed", amt)
|
||||
where reference_type = "Expense Claim" and
|
||||
reference_name = %s and docstatus = 1""", d.reference_name ,as_dict=1)[0].amt
|
||||
frappe.db.set_value("Expense Claim", d.reference_name , "total_amount_reimbursed", amt)
|
||||
|
||||
def validate_expense_claim(self):
|
||||
for d in self.accounts:
|
||||
if d.against_expense_claim:
|
||||
if d.reference_type=="Expense Claim":
|
||||
sanctioned_amount, reimbursed_amount = frappe.db.get_value("Expense Claim",
|
||||
d.against_expense_claim, ("total_sanctioned_amount", "total_amount_reimbursed"))
|
||||
d.reference_name, ("total_sanctioned_amount", "total_amount_reimbursed"))
|
||||
pending_amount = flt(sanctioned_amount) - flt(reimbursed_amount)
|
||||
if d.debit > pending_amount:
|
||||
frappe.throw(_("Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2}".format(d.idx, d.against_expense_claim, pending_amount)))
|
||||
frappe.throw(_("Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2}".format(d.idx, d.reference_name, pending_amount)))
|
||||
|
||||
def validate_credit_debit_note(self):
|
||||
if self.stock_entry:
|
||||
@ -467,6 +468,7 @@ def get_default_bank_cash_account(company, voucher_type, mode_of_payment=None):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_payment_entry_from_sales_invoice(sales_invoice):
|
||||
"""Returns new Journal Entry document as dict for given Sales Invoice"""
|
||||
from erpnext.accounts.utils import get_balance_on
|
||||
si = frappe.get_doc("Sales Invoice", sales_invoice)
|
||||
jv = get_payment_entry(si)
|
||||
@ -479,7 +481,8 @@ def get_payment_entry_from_sales_invoice(sales_invoice):
|
||||
jv.get("accounts")[0].balance = get_balance_on(si.debit_to)
|
||||
jv.get("accounts")[0].party_balance = get_balance_on(party=si.customer, party_type="Customer")
|
||||
jv.get("accounts")[0].credit = si.outstanding_amount
|
||||
jv.get("accounts")[0].against_invoice = si.name
|
||||
jv.get("accounts")[0].reference_type = si.doctype
|
||||
jv.get("accounts")[0].reference_name = si.name
|
||||
|
||||
# debit bank
|
||||
jv.get("accounts")[1].debit = si.outstanding_amount
|
||||
@ -488,6 +491,7 @@ def get_payment_entry_from_sales_invoice(sales_invoice):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_payment_entry_from_purchase_invoice(purchase_invoice):
|
||||
"""Returns new Journal Entry document as dict for given Purchase Invoice"""
|
||||
pi = frappe.get_doc("Purchase Invoice", purchase_invoice)
|
||||
jv = get_payment_entry(pi)
|
||||
jv.remark = 'Payment against Purchase Invoice {0}. {1}'.format(pi.name, pi.remarks)
|
||||
@ -499,13 +503,78 @@ def get_payment_entry_from_purchase_invoice(purchase_invoice):
|
||||
jv.get("accounts")[0].balance = get_balance_on(pi.credit_to)
|
||||
jv.get("accounts")[0].party_balance = get_balance_on(party=pi.supplier, party_type="Supplier")
|
||||
jv.get("accounts")[0].debit = pi.outstanding_amount
|
||||
jv.get("accounts")[0].against_voucher = pi.name
|
||||
jv.get("accounts")[0].reference_type = pi.doctype
|
||||
jv.get("accounts")[0].reference_name = pi.name
|
||||
|
||||
# credit bank
|
||||
jv.get("accounts")[1].credit = pi.outstanding_amount
|
||||
|
||||
return jv.as_dict()
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_payment_entry_from_sales_order(sales_order):
|
||||
"""Returns new Journal Entry document as dict for given Sales Order"""
|
||||
from erpnext.accounts.utils import get_balance_on
|
||||
from erpnext.accounts.party import get_party_account
|
||||
so = frappe.get_doc("Sales Order", sales_order)
|
||||
|
||||
if flt(so.per_billed, 2) != 0.0:
|
||||
frappe.throw(_("Can only make payment against unbilled Sales Order"))
|
||||
|
||||
jv = get_payment_entry(so)
|
||||
jv.remark = 'Advance payment received against Sales Order {0}.'.format(so.name)
|
||||
party_account = get_party_account(so.company, so.customer, "Customer")
|
||||
|
||||
amount = flt(so.base_grand_total) - flt(so.advance_paid)
|
||||
|
||||
# credit customer
|
||||
jv.get("accounts")[0].account = party_account
|
||||
jv.get("accounts")[0].party_type = "Customer"
|
||||
jv.get("accounts")[0].party = so.customer
|
||||
jv.get("accounts")[0].balance = get_balance_on(party_account)
|
||||
jv.get("accounts")[0].party_balance = get_balance_on(party=so.customer, party_type="Customer")
|
||||
jv.get("accounts")[0].credit = amount
|
||||
jv.get("accounts")[0].reference_type = so.doctype
|
||||
jv.get("accounts")[0].reference_name = so.name
|
||||
jv.get("accounts")[0].is_advance = "Yes"
|
||||
|
||||
# debit bank
|
||||
jv.get("accounts")[1].debit = amount
|
||||
|
||||
return jv.as_dict()
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_payment_entry_from_purchase_order(purchase_order):
|
||||
"""Returns new Journal Entry document as dict for given Sales Order"""
|
||||
from erpnext.accounts.utils import get_balance_on
|
||||
from erpnext.accounts.party import get_party_account
|
||||
po = frappe.get_doc("Purchase Order", purchase_order)
|
||||
|
||||
if flt(po.per_billed, 2) != 0.0:
|
||||
frappe.throw(_("Can only make payment against unbilled Sales Order"))
|
||||
|
||||
jv = get_payment_entry(po)
|
||||
jv.remark = 'Advance payment made against Purchase Order {0}.'.format(po.name)
|
||||
party_account = get_party_account(po.company, po.supplier, "Supplier")
|
||||
|
||||
amount = flt(po.base_grand_total) - flt(po.advance_paid)
|
||||
|
||||
# credit customer
|
||||
jv.get("accounts")[0].account = party_account
|
||||
jv.get("accounts")[0].party_type = "Supplier"
|
||||
jv.get("accounts")[0].party = po.supplier
|
||||
jv.get("accounts")[0].balance = get_balance_on(party_account)
|
||||
jv.get("accounts")[0].party_balance = get_balance_on(party=po.supplier, party_type="Supplier")
|
||||
jv.get("accounts")[0].debit = amount
|
||||
jv.get("accounts")[0].reference_type = po.doctype
|
||||
jv.get("accounts")[0].reference_name = po.name
|
||||
jv.get("accounts")[0].is_advance = "Yes"
|
||||
|
||||
# debit bank
|
||||
jv.get("accounts")[1].credit = amount
|
||||
|
||||
return jv.as_dict()
|
||||
|
||||
def get_payment_entry(doc):
|
||||
bank_account = get_default_bank_cash_account(doc.company, "Bank Entry")
|
||||
|
||||
@ -536,13 +605,14 @@ def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
|
||||
return frappe.db.sql("""select jv.name, jv.posting_date, jv.user_remark
|
||||
from `tabJournal Entry` jv, `tabJournal Entry Account` jv_detail
|
||||
where jv_detail.parent = jv.name and jv_detail.account = %s and ifnull(jv_detail.party, '') = %s
|
||||
and (ifnull(jv_detail.against_invoice, '') = '' and ifnull(jv_detail.against_voucher, '') = ''
|
||||
and ifnull(jv_detail.against_jv, '') = '' )
|
||||
and (ifnull(jv_detail.reference_type, '') = ''
|
||||
and jv.docstatus = 1 and jv.{0} like %s order by jv.name desc limit %s, %s""".format(searchfield),
|
||||
(filters.get("account"), cstr(filters.get("party")), "%{0}%".format(txt), start, page_len))
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_outstanding(args):
|
||||
if not frappe.has_permission("Account"):
|
||||
frappe.msgprint(_("No Permission"), raise_exception=1)
|
||||
args = eval(args)
|
||||
if args.get("doctype") == "Journal Entry":
|
||||
condition = " and party=%(party)s" if args.get("party") else ""
|
||||
@ -550,8 +620,7 @@ def get_outstanding(args):
|
||||
against_jv_amount = frappe.db.sql("""
|
||||
select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0))
|
||||
from `tabJournal Entry Account` where parent=%(docname)s and account=%(account)s {0}
|
||||
and ifnull(against_invoice, '')='' and ifnull(against_voucher, '')=''
|
||||
and ifnull(against_jv, '')=''""".format(condition), args)
|
||||
and ifnull(reference_type, '')=''""".format(condition), args)
|
||||
|
||||
against_jv_amount = flt(against_jv_amount[0][0]) if against_jv_amount else 0
|
||||
return {
|
||||
@ -570,6 +639,9 @@ def get_outstanding(args):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_party_account_and_balance(company, party_type, party):
|
||||
if not frappe.has_permission("Account"):
|
||||
frappe.msgprint(_("No Permission"), raise_exception=1)
|
||||
|
||||
from erpnext.accounts.party import get_party_account
|
||||
account = get_party_account(company, party, party_type)
|
||||
|
||||
@ -581,3 +653,16 @@ def get_party_account_and_balance(company, party_type, party):
|
||||
"balance": account_balance,
|
||||
"party_balance": party_balance
|
||||
}
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_account_balance_and_party_type(account, date):
|
||||
"""Returns dict of account balance and party type to be set in Journal Entry on selection of account."""
|
||||
if not frappe.has_permission("Account"):
|
||||
frappe.msgprint(_("No Permission"), raise_exception=1)
|
||||
|
||||
account_type = frappe.db.get_value("Account", account, "account_type")
|
||||
return {
|
||||
"balance": get_balance_on(account, date),
|
||||
"party_type": {"Receivable":"Customer", "Payable":"Supplier"}.get(account_type, "")
|
||||
}
|
||||
|
||||
|
@ -29,10 +29,6 @@ class TestJournalEntry(unittest.TestCase):
|
||||
|
||||
def jv_against_voucher_testcase(self, base_jv, test_voucher):
|
||||
dr_or_cr = "credit" if test_voucher.doctype in ["Sales Order", "Journal Entry"] else "debit"
|
||||
field_dict = {'Journal Entry': "against_jv",
|
||||
'Sales Order': "against_sales_order",
|
||||
'Purchase Order': "against_purchase_order"
|
||||
}
|
||||
|
||||
test_voucher.insert()
|
||||
test_voucher.submit()
|
||||
@ -42,21 +38,20 @@ class TestJournalEntry(unittest.TestCase):
|
||||
where account = %s and docstatus = 1 and parent = %s""",
|
||||
("_Test Receivable - _TC", test_voucher.name)))
|
||||
|
||||
self.assertTrue(not frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||
where %s=%s""" % (field_dict.get(test_voucher.doctype), '%s'), (test_voucher.name)))
|
||||
self.assertFalse(frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||
where reference_type = %s and reference_name = %s""", (test_voucher.doctype, test_voucher.name)))
|
||||
|
||||
base_jv.get("accounts")[0].is_advance = "Yes" if (test_voucher.doctype in ["Sales Order", "Purchase Order"]) else "No"
|
||||
base_jv.get("accounts")[0].set(field_dict.get(test_voucher.doctype), test_voucher.name)
|
||||
base_jv.get("accounts")[0].set("reference_type", test_voucher.doctype)
|
||||
base_jv.get("accounts")[0].set("reference_name", test_voucher.name)
|
||||
base_jv.insert()
|
||||
base_jv.submit()
|
||||
|
||||
submitted_voucher = frappe.get_doc(test_voucher.doctype, test_voucher.name)
|
||||
|
||||
self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||
where %s=%s""" % (field_dict.get(test_voucher.doctype), '%s'), (submitted_voucher.name)))
|
||||
|
||||
self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||
where %s=%s and %s=400""" % (field_dict.get(submitted_voucher.doctype), '%s', dr_or_cr), (submitted_voucher.name)))
|
||||
where reference_type = %s and reference_name = %s and {0}=400""".format(dr_or_cr),
|
||||
(submitted_voucher.doctype, submitted_voucher.name)))
|
||||
|
||||
if base_jv.get("accounts")[0].is_advance == "Yes":
|
||||
self.advance_paid_testcase(base_jv, submitted_voucher, dr_or_cr)
|
||||
@ -74,8 +69,8 @@ class TestJournalEntry(unittest.TestCase):
|
||||
if test_voucher.doctype == "Journal Entry":
|
||||
# if test_voucher is a Journal Entry, test cancellation of test_voucher
|
||||
test_voucher.cancel()
|
||||
self.assertTrue(not frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||
where against_jv=%s""", test_voucher.name))
|
||||
self.assertFalse(frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||
where reference_type='Journal Entry' and reference_name=%s""", test_voucher.name))
|
||||
|
||||
elif test_voucher.doctype in ["Sales Order", "Purchase Order"]:
|
||||
# if test_voucher is a Sales Order/Purchase Order, test error on cancellation of test_voucher
|
||||
|
@ -1,27 +1,44 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "hash",
|
||||
"creation": "2013-02-22 01:27:39",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "account",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Account",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "account",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Account",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_width": "250px",
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 1,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "250px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "balance",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Account Balance",
|
||||
"no_copy": 1,
|
||||
@ -30,186 +47,336 @@
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"default": ":Company",
|
||||
"description": "If Income or Expense",
|
||||
"fieldname": "cost_center",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Cost Center",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "cost_center",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Cost Center",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_width": "180px",
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "180px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "col_break1",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "party_type",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Party Type",
|
||||
"no_copy": 0,
|
||||
"options": "DocType",
|
||||
"permlevel": 0
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "party",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Party",
|
||||
"no_copy": 0,
|
||||
"options": "party_type",
|
||||
"permlevel": 0
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "party_balance",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Party Balance",
|
||||
"no_copy": 0,
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"read_only": 1
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "sec_break1",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Amount",
|
||||
"permlevel": 0
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "debit",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Debit",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "debit",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "col_break2",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "credit",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Credit",
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "credit",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"permlevel": 0
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "reference",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Reference",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "against_invoice",
|
||||
"fieldtype": "Link",
|
||||
"in_filter": 1,
|
||||
"label": "Against Sales Invoice",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "against_invoice",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Sales Invoice",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"search_index": 1
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "against_voucher",
|
||||
"fieldtype": "Link",
|
||||
"in_filter": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Against Purchase Invoice",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "against_voucher",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Purchase Invoice",
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "reference_type",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Reference Type",
|
||||
"no_copy": 0,
|
||||
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"search_index": 1
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "against_jv",
|
||||
"fieldtype": "Link",
|
||||
"in_filter": 1,
|
||||
"label": "Against Journal Entry",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "against_jv",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Journal Entry",
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "reference_name",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Reference Name",
|
||||
"no_copy": 0,
|
||||
"options": "reference_type",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"search_index": 1
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "col_break3",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "against_sales_order",
|
||||
"fieldtype": "Link",
|
||||
"label": "Against Sales Order",
|
||||
"options": "Sales Order",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "against_purchase_order",
|
||||
"fieldtype": "Link",
|
||||
"label": "Against Purchase Order",
|
||||
"options": "Purchase Order",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "against_expense_claim",
|
||||
"fieldtype": "Link",
|
||||
"label": "Against Expense Claim",
|
||||
"options": "Expense Claim",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": ""
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "is_advance",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Is Advance",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "is_advance",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "No\nYes",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1
|
||||
"print_hide": 1,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"fieldname": "against_account",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Against Account",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "against_account",
|
||||
"oldfieldtype": "Text",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1
|
||||
"print_hide": 1,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 1,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-02-19 01:07:00.388689",
|
||||
"modified": "2015-08-11 10:44:11.432623",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Journal Entry Account",
|
||||
"owner": "Administrator",
|
||||
"permissions": []
|
||||
"permissions": [],
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0
|
||||
}
|
@ -34,8 +34,8 @@ class PaymentReconciliation(Document):
|
||||
t1.name = t2.parent and t1.docstatus = 1 and t2.docstatus = 1
|
||||
and t2.party_type = %(party_type)s and t2.party = %(party)s
|
||||
and t2.account = %(account)s and {dr_or_cr} > 0
|
||||
and ifnull(t2.against_voucher, '')='' and ifnull(t2.against_invoice, '')=''
|
||||
and ifnull(t2.against_jv, '')='' {cond}
|
||||
and ifnull(t2.reference_type, '') in ('', 'Sales Order', 'Purchase Order')
|
||||
{cond}
|
||||
and (CASE
|
||||
WHEN t1.voucher_type in ('Debit Note', 'Credit Note')
|
||||
THEN 1=1
|
||||
|
@ -12,13 +12,6 @@ class PaymentTool(Document):
|
||||
def make_journal_entry(self):
|
||||
from erpnext.accounts.utils import get_balance_on
|
||||
total_payment_amount = 0.00
|
||||
invoice_voucher_type = {
|
||||
'Sales Invoice': 'against_invoice',
|
||||
'Purchase Invoice': 'against_voucher',
|
||||
'Journal Entry': 'against_jv',
|
||||
'Sales Order': 'against_sales_order',
|
||||
'Purchase Order': 'against_purchase_order',
|
||||
}
|
||||
|
||||
jv = frappe.new_doc('Journal Entry')
|
||||
jv.voucher_type = 'Journal Entry'
|
||||
@ -41,7 +34,8 @@ class PaymentTool(Document):
|
||||
d1.party = self.party
|
||||
d1.balance = get_balance_on(self.party_account)
|
||||
d1.set("debit" if self.received_or_paid=="Paid" else "credit", flt(v.payment_amount))
|
||||
d1.set(invoice_voucher_type.get(v.against_voucher_type), v.against_voucher_no)
|
||||
d1.set("reference_type", v.against_voucher_type)
|
||||
d1.set("reference_name", v.against_voucher_no)
|
||||
d1.set('is_advance', 'Yes' if v.against_voucher_type in ['Sales Order', 'Purchase Order'] else 'No')
|
||||
total_payment_amount = flt(total_payment_amount) + flt(d1.debit) - flt(d1.credit)
|
||||
|
||||
|
@ -26,7 +26,8 @@ class TestPaymentTool(unittest.TestCase):
|
||||
|
||||
self.create_against_jv(jv_test_records[0], {
|
||||
"party": "_Test Customer 3",
|
||||
"against_sales_order": so1.name,
|
||||
"reference_type": "Sales Order",
|
||||
"reference_name": so1.name,
|
||||
"is_advance": "Yes"
|
||||
})
|
||||
|
||||
@ -36,7 +37,8 @@ class TestPaymentTool(unittest.TestCase):
|
||||
|
||||
self.create_against_jv(jv_test_records[0], {
|
||||
"party": "_Test Customer 3",
|
||||
"against_sales_order": so2.name,
|
||||
"reference_type": "Sales Order",
|
||||
"reference_name": so2.name,
|
||||
"credit": 1000,
|
||||
"is_advance": "Yes"
|
||||
})
|
||||
@ -52,7 +54,8 @@ class TestPaymentTool(unittest.TestCase):
|
||||
|
||||
self.create_against_jv(jv_test_records[0], {
|
||||
"party": "_Test Customer 3",
|
||||
"against_invoice": si1.name
|
||||
"reference_type": si1.doctype,
|
||||
"reference_name": si1.name
|
||||
})
|
||||
#Create SI with no outstanding
|
||||
si2 = self.create_voucher(si_test_records[0], {
|
||||
@ -62,7 +65,8 @@ class TestPaymentTool(unittest.TestCase):
|
||||
|
||||
self.create_against_jv(jv_test_records[0], {
|
||||
"party": "_Test Customer 3",
|
||||
"against_invoice": si2.name,
|
||||
"reference_type": si2.doctype,
|
||||
"reference_name": si2.name,
|
||||
"credit": 561.80
|
||||
})
|
||||
|
||||
@ -153,29 +157,12 @@ class TestPaymentTool(unittest.TestCase):
|
||||
|
||||
new_jv = paytool.make_journal_entry()
|
||||
|
||||
#Create a list of expected values as [party account, payment against, against_jv, against_invoice,
|
||||
#against_voucher, against_sales_order, against_purchase_order]
|
||||
expected_values = [
|
||||
[paytool.party_account, paytool.party, 100.00, expected_outstanding.get("Journal Entry")[0], None, None, None, None],
|
||||
[paytool.party_account, paytool.party, 100.00, None, expected_outstanding.get("Sales Invoice")[0], None, None, None],
|
||||
[paytool.party_account, paytool.party, 100.00, None, None, expected_outstanding.get("Purchase Invoice")[0], None, None],
|
||||
[paytool.party_account, paytool.party, 100.00, None, None, None, expected_outstanding.get("Sales Order")[0], None],
|
||||
[paytool.party_account, paytool.party, 100.00, None, None, None, None, expected_outstanding.get("Purchase Order")[0]]
|
||||
]
|
||||
|
||||
for jv_entry in new_jv.get("accounts"):
|
||||
if paytool.party_account == jv_entry.get("account") and paytool.party == jv_entry.get("party"):
|
||||
row = [
|
||||
jv_entry.get("account"),
|
||||
jv_entry.get("party"),
|
||||
jv_entry.get("debit" if paytool.party_type=="Supplier" else "credit"),
|
||||
jv_entry.get("against_jv"),
|
||||
jv_entry.get("against_invoice"),
|
||||
jv_entry.get("against_voucher"),
|
||||
jv_entry.get("against_sales_order"),
|
||||
jv_entry.get("against_purchase_order"),
|
||||
]
|
||||
self.assertTrue(row in expected_values)
|
||||
self.assertEquals(100.00,
|
||||
jv_entry.get("debit" if paytool.party_type=="Supplier" else "credit"))
|
||||
self.assertEquals(jv_entry.reference_name,
|
||||
expected_outstanding[jv_entry.reference_type][0])
|
||||
|
||||
self.assertEquals(new_jv.get("cheque_no"), paytool.reference_no)
|
||||
self.assertEquals(new_jv.get("cheque_date"), paytool.reference_date)
|
||||
|
@ -25,10 +25,9 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
||||
if(!doc.is_return) {
|
||||
if(doc.docstatus==1) {
|
||||
if(doc.outstanding_amount > 0) {
|
||||
this.frm.add_custom_button(__('Make Payment Entry'), this.make_bank_entry);
|
||||
this.frm.add_custom_button(__('Payment'), this.make_bank_entry).addClass("btn-primary");
|
||||
}
|
||||
|
||||
cur_frm.add_custom_button(__('Make Debit Note'), this.make_debit_note);
|
||||
cur_frm.add_custom_button(__('Debit Note'), this.make_debit_note);
|
||||
}
|
||||
|
||||
if(doc.docstatus===0) {
|
||||
|
@ -41,7 +41,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.po_required()
|
||||
self.pr_required()
|
||||
self.validate_supplier_invoice()
|
||||
self.validate_advance_jv("advances", "purchase_order")
|
||||
self.validate_advance_jv("Purchase Order")
|
||||
|
||||
self.check_active_purchase_items()
|
||||
self.check_conversion_rate()
|
||||
@ -365,7 +365,7 @@ class PurchaseInvoice(BuyingController):
|
||||
def on_cancel(self):
|
||||
if not self.is_return:
|
||||
from erpnext.accounts.utils import remove_against_link_from_jv
|
||||
remove_against_link_from_jv(self.doctype, self.name, "against_voucher")
|
||||
remove_against_link_from_jv(self.doctype, self.name)
|
||||
|
||||
self.update_prevdoc_status()
|
||||
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
||||
|
@ -218,17 +218,14 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
pi.load_from_db()
|
||||
|
||||
self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||
where against_voucher=%s""", pi.name))
|
||||
|
||||
self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||
where against_voucher=%s and debit=300""", pi.name))
|
||||
where reference_type='Purchase Invoice' and reference_name=%s and debit=300""", pi.name))
|
||||
|
||||
self.assertEqual(pi.outstanding_amount, 1212.30)
|
||||
|
||||
pi.cancel()
|
||||
|
||||
self.assertTrue(not frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||
where against_voucher=%s""", pi.name))
|
||||
self.assertFalse(frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||
where reference_type='Purchase Invoice' and reference_name=%s""", pi.name))
|
||||
|
||||
def test_recurring_invoice(self):
|
||||
from erpnext.controllers.tests.test_recurring_document import test_recurring_document
|
||||
|
@ -48,6 +48,9 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
if(doc.update_stock) this.show_stock_ledger();
|
||||
|
||||
if(doc.docstatus==1 && !doc.is_return) {
|
||||
cur_frm.add_custom_button(doc.update_stock ? __('Sales Return') : __('Credit Note'),
|
||||
this.make_sales_return);
|
||||
|
||||
if(cint(doc.update_stock)!=1) {
|
||||
// show Make Delivery Note button only if Sales Invoice is not created from Delivery Note
|
||||
var from_delivery_note = false;
|
||||
@ -57,16 +60,13 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
});
|
||||
|
||||
if(!from_delivery_note) {
|
||||
cur_frm.add_custom_button(__('Make Delivery'), cur_frm.cscript['Make Delivery Note'])
|
||||
cur_frm.add_custom_button(__('Delivery'), cur_frm.cscript['Make Delivery Note']).addClass("btn-primary");
|
||||
}
|
||||
}
|
||||
|
||||
if(doc.outstanding_amount!=0 && !cint(doc.is_return)) {
|
||||
cur_frm.add_custom_button(__('Make Payment Entry'), cur_frm.cscript.make_bank_entry);
|
||||
cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry).addClass("btn-primary");
|
||||
}
|
||||
|
||||
cur_frm.add_custom_button(doc.update_stock ? __('Make Sales Return') : __('Make Credit Note'),
|
||||
this.make_sales_return);
|
||||
}
|
||||
|
||||
// Show buttons only when pos view is active
|
||||
|
@ -46,7 +46,7 @@ class SalesInvoice(SellingController):
|
||||
self.validate_debit_to_acc()
|
||||
self.validate_fixed_asset_account()
|
||||
self.clear_unallocated_advances("Sales Invoice Advance", "advances")
|
||||
self.validate_advance_jv("advances", "sales_order")
|
||||
self.validate_advance_jv("Sales Order")
|
||||
self.add_remarks()
|
||||
self.validate_write_off_account()
|
||||
|
||||
@ -105,7 +105,7 @@ class SalesInvoice(SellingController):
|
||||
self.check_stop_sales_order("sales_order")
|
||||
|
||||
from erpnext.accounts.utils import remove_against_link_from_jv
|
||||
remove_against_link_from_jv(self.doctype, self.name, "against_invoice")
|
||||
remove_against_link_from_jv(self.doctype, self.name)
|
||||
|
||||
if not self.is_return:
|
||||
self.update_status_updater_args()
|
||||
|
@ -391,7 +391,8 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
import test_records as jv_test_records
|
||||
|
||||
jv = frappe.get_doc(frappe.copy_doc(jv_test_records[0]))
|
||||
jv.get("accounts")[0].against_invoice = w.name
|
||||
jv.get("accounts")[0].reference_type = w.doctype
|
||||
jv.get("accounts")[0].reference_name = w.name
|
||||
jv.insert()
|
||||
jv.submit()
|
||||
|
||||
@ -656,17 +657,17 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
si.load_from_db()
|
||||
|
||||
self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||
where against_invoice=%s""", si.name))
|
||||
where reference_name=%s""", si.name))
|
||||
|
||||
self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||
where against_invoice=%s and credit=300""", si.name))
|
||||
where reference_name=%s and credit=300""", si.name))
|
||||
|
||||
self.assertEqual(si.outstanding_amount, 261.8)
|
||||
|
||||
si.cancel()
|
||||
|
||||
self.assertTrue(not frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||
where against_invoice=%s""", si.name))
|
||||
where reference_name=%s""", si.name))
|
||||
|
||||
def test_recurring_invoice(self):
|
||||
from erpnext.controllers.tests.test_recurring_document import test_recurring_document
|
||||
@ -745,7 +746,6 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
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)
|
||||
|
||||
actual_qty_0 = get_qty_after_transaction()
|
||||
|
@ -202,7 +202,7 @@ erpnext.AccountsChart = Class.extend({
|
||||
title:__('New Account'),
|
||||
fields: [
|
||||
{fieldtype:'Data', fieldname:'account_name', label:__('New Account Name'), reqd:true,
|
||||
description: __("Name of new Account. Note: Please don't create accounts for Customers and Suppliers, they are created automatically from the Customer and Supplier master")},
|
||||
description: __("Name of new Account. Note: Please don't create accounts for Customers and Suppliers")},
|
||||
{fieldtype:'Check', fieldname:'is_group', label:__('Is Group'),
|
||||
description: __('Further accounts can be made under Groups, but entries can be made against non-Groups')},
|
||||
{fieldtype:'Select', fieldname:'account_type', label:__('Account Type'),
|
||||
|
@ -15,21 +15,19 @@ def execute(filters=None):
|
||||
entries = get_entries(filters)
|
||||
invoice_posting_date_map = get_invoice_posting_date_map(filters)
|
||||
against_date = ""
|
||||
outstanding_amount = 0.0
|
||||
|
||||
data = []
|
||||
for d in entries:
|
||||
if d.against_voucher:
|
||||
against_date = d.against_voucher and invoice_posting_date_map[d.against_voucher] or ""
|
||||
against_date = invoice_posting_date_map[d.reference_name] or ""
|
||||
if d.reference_type=="Purchase Invoice":
|
||||
payment_amount = flt(d.debit) or -1 * flt(d.credit)
|
||||
else:
|
||||
against_date = d.against_invoice and invoice_posting_date_map[d.against_invoice] or ""
|
||||
payment_amount = flt(d.credit) or -1 * flt(d.debit)
|
||||
|
||||
row = [d.name, d.party_type, d.party, d.posting_date, d.against_voucher or d.against_invoice,
|
||||
row = [d.name, d.party_type, d.party, d.posting_date, d.reference_name,
|
||||
against_date, d.debit, d.credit, d.cheque_no, d.cheque_date, d.remark]
|
||||
|
||||
if d.against_voucher or d.against_invoice:
|
||||
if d.reference_name:
|
||||
row += get_ageing_data(30, 60, 90, d.posting_date, against_date, payment_amount)
|
||||
else:
|
||||
row += ["", "", "", "", ""]
|
||||
@ -82,7 +80,7 @@ def get_conditions(filters):
|
||||
def get_entries(filters):
|
||||
conditions = get_conditions(filters)
|
||||
entries = frappe.db.sql("""select jv.name, jvd.party_type, jvd.party, jv.posting_date,
|
||||
jvd.against_voucher, jvd.against_invoice, jvd.debit, jvd.credit,
|
||||
jvd.reference_type, jvd.reference_name, jvd.debit, jvd.credit,
|
||||
jv.cheque_no, jv.cheque_date, jv.remark
|
||||
from `tabJournal Entry Account` jvd, `tabJournal Entry` jv
|
||||
where jvd.parent = jv.name and jv.docstatus=1 %s order by jv.name DESC""" %
|
||||
|
@ -142,13 +142,6 @@ def reconcile_against_document(args):
|
||||
for d in args:
|
||||
check_if_jv_modified(d)
|
||||
validate_allocated_amount(d)
|
||||
against_fld = {
|
||||
'Journal Entry' : 'against_jv',
|
||||
'Sales Invoice' : 'against_invoice',
|
||||
'Purchase Invoice' : 'against_voucher'
|
||||
}
|
||||
|
||||
d['against_fld'] = against_fld[d['against_voucher_type']]
|
||||
|
||||
# cancel JV
|
||||
jv_obj = frappe.get_doc('Journal Entry', d['voucher_no'])
|
||||
@ -173,8 +166,7 @@ def check_if_jv_modified(args):
|
||||
select t2.{dr_or_cr} from `tabJournal Entry` t1, `tabJournal Entry Account` t2
|
||||
where t1.name = t2.parent and t2.account = %(account)s
|
||||
and t2.party_type = %(party_type)s and t2.party = %(party)s
|
||||
and ifnull(t2.against_voucher, '')=''
|
||||
and ifnull(t2.against_invoice, '')='' and ifnull(t2.against_jv, '')=''
|
||||
and ifnull(t2.reference_type, '') in ("", "Sales Order", "Purchase Order")
|
||||
and t1.name = %(voucher_no)s and t2.name = %(voucher_detail_no)s
|
||||
and t1.docstatus=1 """.format(dr_or_cr = args.get("dr_or_cr")), args)
|
||||
|
||||
@ -193,7 +185,12 @@ def update_against_doc(d, jv_obj):
|
||||
"""
|
||||
jv_detail = jv_obj.get("accounts", {"name": d["voucher_detail_no"]})[0]
|
||||
jv_detail.set(d["dr_or_cr"], d["allocated_amt"])
|
||||
jv_detail.set(d["against_fld"], d["against_voucher"])
|
||||
|
||||
original_reference_type = jv_detail.reference_type
|
||||
original_reference_name = jv_detail.reference_name
|
||||
|
||||
jv_detail.set("reference_type", d["against_voucher_type"])
|
||||
jv_detail.set("reference_name", d["against_voucher"])
|
||||
|
||||
if d['allocated_amt'] < d['unadjusted_amt']:
|
||||
jvd = frappe.db.sql("""select cost_center, balance, against_account, is_advance
|
||||
@ -208,6 +205,8 @@ def update_against_doc(d, jv_obj):
|
||||
ch.set(d['dr_or_cr'], flt(d['unadjusted_amt']) - flt(d['allocated_amt']))
|
||||
ch.set(d['dr_or_cr']== 'debit' and 'credit' or 'debit', 0)
|
||||
ch.against_account = cstr(jvd[0][2])
|
||||
ch.reference_type = original_reference_type
|
||||
ch.reference_name = original_reference_name
|
||||
ch.is_advance = cstr(jvd[0][3])
|
||||
ch.docstatus = 1
|
||||
|
||||
@ -215,15 +214,16 @@ def update_against_doc(d, jv_obj):
|
||||
jv_obj.flags.ignore_validate_update_after_submit = True
|
||||
jv_obj.save()
|
||||
|
||||
def remove_against_link_from_jv(ref_type, ref_no, against_field):
|
||||
def remove_against_link_from_jv(ref_type, ref_no):
|
||||
linked_jv = frappe.db.sql_list("""select parent from `tabJournal Entry Account`
|
||||
where `%s`=%s and docstatus < 2""" % (against_field, "%s"), (ref_no))
|
||||
where reference_type=%s and reference_name=%s and docstatus < 2""", (ref_type, ref_no))
|
||||
|
||||
if linked_jv:
|
||||
frappe.db.sql("""update `tabJournal Entry Account` set `%s`=null,
|
||||
frappe.db.sql("""update `tabJournal Entry Account`
|
||||
set reference_type=null, reference_name = null,
|
||||
modified=%s, modified_by=%s
|
||||
where `%s`=%s and docstatus < 2""" % (against_field, "%s", "%s", against_field, "%s"),
|
||||
(now(), frappe.session.user, ref_no))
|
||||
where reference_type=%s and reference_name=%s
|
||||
and docstatus < 2""", (now(), frappe.session.user, ref_type, ref_no))
|
||||
|
||||
frappe.db.sql("""update `tabGL Entry`
|
||||
set against_voucher_type=null, against_voucher=null,
|
||||
|
@ -5,6 +5,14 @@ frappe.provide("erpnext.buying");
|
||||
|
||||
{% include 'buying/doctype/purchase_common/purchase_common.js' %};
|
||||
|
||||
frappe.ui.form.on("Purchase Order", {
|
||||
onload: function(frm) {
|
||||
erpnext.queries.setup_queries(frm, "Warehouse", function() {
|
||||
return erpnext.queries.warehouse(frm.doc);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend({
|
||||
refresh: function(doc, cdt, cdn) {
|
||||
var me = this;
|
||||
@ -12,25 +20,32 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
// this.frm.dashboard.reset();
|
||||
|
||||
if(doc.docstatus == 1 && doc.status != 'Stopped') {
|
||||
|
||||
if(flt(doc.per_billed, 2) < 100 || doc.per_received < 100)
|
||||
cur_frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Purchase Order']);
|
||||
|
||||
if(flt(doc.per_billed)==0) {
|
||||
cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry);
|
||||
}
|
||||
|
||||
if(flt(doc.per_received, 2) < 100) {
|
||||
cur_frm.add_custom_button(__('Make Purchase Receipt'), this.make_purchase_receipt);
|
||||
cur_frm.add_custom_button(__('Receive'), this.make_purchase_receipt).addClass("btn-primary");
|
||||
|
||||
if(doc.is_subcontracted==="Yes") {
|
||||
cur_frm.add_custom_button(__('Transfer Material to Supplier'), this.make_stock_entry);
|
||||
}
|
||||
}
|
||||
if(flt(doc.per_billed, 2) < 100)
|
||||
cur_frm.add_custom_button(__('Make Invoice'), this.make_purchase_invoice);
|
||||
|
||||
if(flt(doc.per_billed, 2) < 100 || doc.per_received < 100)
|
||||
cur_frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Purchase Order']);
|
||||
if(flt(doc.per_billed, 2) < 100)
|
||||
cur_frm.add_custom_button(__('Invoice'), this.make_purchase_invoice);
|
||||
|
||||
|
||||
} else if(doc.docstatus===0) {
|
||||
cur_frm.cscript.add_from_mappers();
|
||||
}
|
||||
|
||||
if(doc.docstatus == 1 && doc.status == 'Stopped')
|
||||
cur_frm.add_custom_button(__('Unstop Purchase Order'), cur_frm.cscript['Unstop Purchase Order']);
|
||||
cur_frm.add_custom_button(__('Unstop'), cur_frm.cscript['Unstop Purchase Order']);
|
||||
},
|
||||
|
||||
make_stock_entry: function() {
|
||||
@ -126,8 +141,22 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
items_add: function(doc, cdt, cdn) {
|
||||
var row = frappe.get_doc(cdt, cdn);
|
||||
this.frm.script_manager.copy_from_first_row("items", row, ["schedule_date"]);
|
||||
},
|
||||
|
||||
make_bank_entry: function() {
|
||||
return frappe.call({
|
||||
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_from_purchase_order",
|
||||
args: {
|
||||
"purchase_order": cur_frm.doc.name
|
||||
},
|
||||
callback: function(r) {
|
||||
var doclist = frappe.model.sync(r.message);
|
||||
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// for backward compatibility: combine new and previous states
|
||||
$.extend(cur_frm.cscript, new erpnext.buying.PurchaseOrderController({frm: cur_frm}));
|
||||
|
1
erpnext/change_log/current/journal_entry_rename.md
Normal file
1
erpnext/change_log/current/journal_entry_rename.md
Normal file
@ -0,0 +1 @@
|
||||
- For referencing a line in **Journal Entry**, now you can reference by the **Reference Type** and **Reference Name** columns, instead of "Against Sales Invoice", "Against Purchase Invoice", etc.
|
@ -211,29 +211,32 @@ class AccountsController(TransactionBase):
|
||||
and ifnull(allocated_amount, 0) = 0""" % (childtype, '%s', '%s'), (parentfield, self.name))
|
||||
|
||||
def get_advances(self, account_head, party_type, party, child_doctype, parentfield, dr_or_cr, against_order_field):
|
||||
so_list = list(set([d.get(against_order_field) for d in self.get("items") if d.get(against_order_field)]))
|
||||
cond = ""
|
||||
if so_list:
|
||||
cond = "or (ifnull(t2.%s, '') in (%s))" % ("against_" + against_order_field, ', '.join(['%s']*len(so_list)))
|
||||
"""Returns list of advances against Account, Party, Reference"""
|
||||
order_list = list(set([d.get(against_order_field) for d in self.get("items") if d.get(against_order_field)]))
|
||||
|
||||
if not order_list:
|
||||
return
|
||||
|
||||
in_placeholder = ', '.join(['%s'] * len(order_list))
|
||||
|
||||
# conver sales_order to "Sales Order"
|
||||
reference_type = against_order_field.replace("_", " ").title()
|
||||
|
||||
res = frappe.db.sql("""
|
||||
select
|
||||
t1.name as jv_no, t1.remark, t2.{0} as amount, t2.name as jv_detail_no, `against_{1}` as against_order
|
||||
t1.name as jv_no, t1.remark, t2.{0} as amount, t2.name as jv_detail_no,
|
||||
reference_name as against_order
|
||||
from
|
||||
`tabJournal Entry` t1, `tabJournal Entry Account` t2
|
||||
where
|
||||
t1.name = t2.parent and t2.account = %s
|
||||
and t2.party_type = %s and t2.party = %s
|
||||
and t2.is_advance = 'Yes' and t1.docstatus = 1
|
||||
and ((
|
||||
ifnull(t2.against_voucher, '') = ''
|
||||
and ifnull(t2.against_invoice, '') = ''
|
||||
and ifnull(t2.against_jv, '') = ''
|
||||
and ifnull(t2.against_sales_order, '') = ''
|
||||
and ifnull(t2.against_purchase_order, '') = ''
|
||||
) {2})
|
||||
order by t1.posting_date""".format(dr_or_cr, against_order_field, cond),
|
||||
[account_head, party_type, party] + so_list, as_dict=1)
|
||||
and (
|
||||
ifnull(t2.reference_type, '')=''
|
||||
or (t2.reference_type = %s and ifnull(t2.reference_name, '') in ({1})))
|
||||
order by t1.posting_date""".format(dr_or_cr, in_placeholder),
|
||||
[account_head, party_type, party, reference_type] + order_list, as_dict=1)
|
||||
|
||||
self.set(parentfield, [])
|
||||
for d in res:
|
||||
@ -246,25 +249,26 @@ class AccountsController(TransactionBase):
|
||||
"allocated_amount": flt(d.amount) if d.against_order else 0
|
||||
})
|
||||
|
||||
def validate_advance_jv(self, advance_table_fieldname, against_order_field):
|
||||
def validate_advance_jv(self, reference_type):
|
||||
against_order_field = frappe.scrub(reference_type)
|
||||
order_list = list(set([d.get(against_order_field) for d in self.get("items") if d.get(against_order_field)]))
|
||||
if order_list:
|
||||
account = self.get("debit_to" if self.doctype=="Sales Invoice" else "credit_to")
|
||||
|
||||
jv_against_order = frappe.db.sql("""select parent, %s as against_order
|
||||
jv_against_order = frappe.db.sql("""select parent, reference_name as against_order
|
||||
from `tabJournal Entry Account`
|
||||
where docstatus=1 and account=%s and ifnull(is_advance, 'No') = 'Yes'
|
||||
and ifnull(against_sales_order, '') in (%s)
|
||||
group by parent, against_sales_order""" %
|
||||
("against_" + against_order_field, '%s', ', '.join(['%s']*len(order_list))),
|
||||
tuple([account] + order_list), as_dict=1)
|
||||
and reference_type=%s
|
||||
and ifnull(reference_name, '') in ({0})
|
||||
group by parent, reference_name""".format(', '.join(['%s']*len(order_list))),
|
||||
tuple([account, reference_type] + order_list), as_dict=1)
|
||||
|
||||
if jv_against_order:
|
||||
order_jv_map = {}
|
||||
for d in jv_against_order:
|
||||
order_jv_map.setdefault(d.against_order, []).append(d.parent)
|
||||
|
||||
advance_jv_against_si = [d.journal_entry for d in self.get(advance_table_fieldname)]
|
||||
advance_jv_against_si = [d.journal_entry for d in self.get("advances")]
|
||||
|
||||
for order, jv_list in order_jv_map.items():
|
||||
for jv in jv_list:
|
||||
@ -318,10 +322,8 @@ class AccountsController(TransactionBase):
|
||||
def set_total_advance_paid(self):
|
||||
if self.doctype == "Sales Order":
|
||||
dr_or_cr = "credit"
|
||||
against_field = "against_sales_order"
|
||||
else:
|
||||
dr_or_cr = "debit"
|
||||
against_field = "against_purchase_order"
|
||||
|
||||
advance_paid = frappe.db.sql("""
|
||||
select
|
||||
@ -329,8 +331,10 @@ class AccountsController(TransactionBase):
|
||||
from
|
||||
`tabJournal Entry Account`
|
||||
where
|
||||
{against_field} = %s and docstatus = 1 and is_advance = "Yes" """.format(dr_or_cr=dr_or_cr, \
|
||||
against_field=against_field), self.name)
|
||||
reference_type = %s and
|
||||
reference_name = %s and
|
||||
docstatus = 1 and is_advance = "Yes" """.format(dr_or_cr=dr_or_cr),
|
||||
(self.doctype, self.name))
|
||||
|
||||
if advance_paid:
|
||||
advance_paid = flt(advance_paid[0][0], self.precision("advance_paid"))
|
||||
|
@ -24,13 +24,15 @@ erpnext.hr.ExpenseClaimController = frappe.ui.form.Controller.extend({
|
||||
var d1 = frappe.model.add_child(jv, 'Journal Entry Account', 'accounts');
|
||||
d1.debit = expense[i].sanctioned_amount;
|
||||
d1.account = expense[i].default_account;
|
||||
d1.against_expense_claim = cur_frm.doc.name;
|
||||
d1.reference_type = cur_frm.doc.doctype;
|
||||
d1.reference_name = cur_frm.doc.name;
|
||||
}
|
||||
|
||||
// credit to bank
|
||||
var d1 = frappe.model.add_child(jv, 'Journal Entry Account', 'accounts');
|
||||
d1.credit = cur_frm.doc.total_sanctioned_amount;
|
||||
d1.against_expense_claim = cur_frm.doc.name;
|
||||
d1.reference_type = cur_frm.doc.doctype;
|
||||
d1.reference_name = cur_frm.doc.name;
|
||||
if(r.message) {
|
||||
d1.account = r.message.account;
|
||||
d1.balance = r.message.balance;
|
||||
|
@ -4,6 +4,14 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cint
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
class ManufacturingSettings(Document):
|
||||
pass
|
||||
|
||||
def get_mins_between_operations():
|
||||
if not hasattr(frappe.local, "_mins_between_operations"):
|
||||
frappe.local._mins_between_operations = cint(frappe.db.get_single_value("Manufacturing Settings",
|
||||
"mins_between_operations")) or 10
|
||||
return relativedelta(minutes=frappe.local._mins_between_operations)
|
||||
|
@ -13,6 +13,7 @@ from erpnext.stock.doctype.item.item import validate_end_of_life
|
||||
from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError, NotInWorkingHoursError
|
||||
from erpnext.projects.doctype.time_log.time_log import OverlapError
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry import get_additional_costs
|
||||
from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
|
||||
|
||||
class OverProductionError(frappe.ValidationError): pass
|
||||
class StockOverProductionError(frappe.ValidationError): pass
|
||||
@ -231,6 +232,7 @@ class ProductionOrder(Document):
|
||||
original_start_time = time_log.from_time
|
||||
while True:
|
||||
_from_time = time_log.from_time
|
||||
|
||||
try:
|
||||
time_log.save()
|
||||
break
|
||||
@ -248,6 +250,7 @@ class ProductionOrder(Document):
|
||||
frappe.msgprint(_("Unable to find Time Slot in the next {0} days for Operation {1}").format(plan_days, d.operation))
|
||||
break
|
||||
|
||||
# if time log needs to be moved, make sure that the from time is not the same
|
||||
if _from_time == time_log.from_time:
|
||||
frappe.throw("Capacity Planning Error")
|
||||
|
||||
@ -273,19 +276,13 @@ class ProductionOrder(Document):
|
||||
d.planned_start_time = self.planned_start_date
|
||||
else:
|
||||
d.planned_start_time = get_datetime(self.operations[i-1].planned_end_time)\
|
||||
+ self.get_mins_between_operations()
|
||||
+ get_mins_between_operations()
|
||||
|
||||
d.planned_end_time = get_datetime(d.planned_start_time) + relativedelta(minutes = d.time_in_mins)
|
||||
|
||||
if d.planned_start_time == d.planned_end_time:
|
||||
frappe.throw(_("Capacity Planning Error"))
|
||||
|
||||
def get_mins_between_operations(self):
|
||||
if not hasattr(self, "_mins_between_operations"):
|
||||
self._mins_between_operations = cint(frappe.db.get_single_value("Manufacturing Settings",
|
||||
"mins_between_operations")) or 10
|
||||
return relativedelta(minutes=self._mins_between_operations)
|
||||
|
||||
def check_operation_fits_in_working_hours(self, d):
|
||||
"""Raises expection if operation is longer than working hours in the given workstation."""
|
||||
from erpnext.manufacturing.doctype.workstation.workstation import check_if_within_operating_hours
|
||||
|
14
erpnext/patches/v5_4/cleanup_journal_entry.py
Normal file
14
erpnext/patches/v5_4/cleanup_journal_entry.py
Normal file
@ -0,0 +1,14 @@
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
for doctype, fieldname in (
|
||||
("Sales Order", "against_sales_order"),
|
||||
("Purchase Order", "against_purchase_order"),
|
||||
("Sales Invoice", "against_invoice"),
|
||||
("Purchase Invoice", "against_voucher"),
|
||||
("Journal Entry", "against_jv"),
|
||||
("Expense Claim", "against_expense_claim"),
|
||||
):
|
||||
frappe.db.update("""update `tabJournal Entry Detail`
|
||||
set reference_type=%s and reference_name={0} where ifnull({0}, '') != ''
|
||||
""".format(fieldname), doctype)
|
@ -6,6 +6,7 @@ import frappe, json
|
||||
from frappe import _
|
||||
from frappe.utils import cstr, flt, get_datetime, get_time, getdate
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
|
||||
|
||||
class OverlapError(frappe.ValidationError): pass
|
||||
class OverProductionLoggedError(frappe.ValidationError): pass
|
||||
@ -182,9 +183,14 @@ class TimeLog(Document):
|
||||
|
||||
def move_to_next_non_overlapping_slot(self):
|
||||
"""If in overlap, set start as the end point of the overlapping time log"""
|
||||
overlapping = self.get_overlap_for("workstation")
|
||||
if overlapping:
|
||||
self.from_time = get_datetime(overlapping.to_time) + relativedelta(minutes=10)
|
||||
overlapping = self.get_overlap_for("workstation") \
|
||||
or self.get_overlap_for("employee") \
|
||||
or self.get_overlap_for("user")
|
||||
|
||||
if not overlapping:
|
||||
frappe.throw("Logical error: Must find overlapping")
|
||||
|
||||
self.from_time = get_datetime(overlapping.to_time) + get_mins_between_operations()
|
||||
|
||||
def get_time_log_summary(self):
|
||||
"""Returns 'Actual Operating Time'. """
|
||||
|
@ -12,31 +12,8 @@ erpnext.stock.StockController = frappe.ui.form.Controller.extend({
|
||||
},
|
||||
|
||||
setup_warehouse_query: function() {
|
||||
var me = this;
|
||||
var warehouse_query_method = function() {
|
||||
erpnext.queries.setup_queries(me.frm, "Warehouse", function() {
|
||||
return erpnext.queries.warehouse(me.frm.doc);
|
||||
};
|
||||
|
||||
var _set_warehouse_query = function(doctype, parentfield) {
|
||||
var warehouse_link_fields = frappe.meta.get_docfields(doctype, me.frm.doc.name,
|
||||
{"fieldtype": "Link", "options": "Warehouse"});
|
||||
$.each(warehouse_link_fields, function(i, df) {
|
||||
if(parentfield) {
|
||||
me.frm.set_query(df.fieldname, parentfield, warehouse_query_method);
|
||||
} else {
|
||||
me.frm.set_query(df.fieldname, warehouse_query_method);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
_set_warehouse_query(me.frm.doc.doctype);
|
||||
|
||||
// warehouse field in tables
|
||||
var table_fields = frappe.meta.get_docfields(me.frm.doc.doctype, me.frm.doc.name,
|
||||
{"fieldtype": "Table"});
|
||||
|
||||
$.each(table_fields, function(i, df) {
|
||||
_set_warehouse_query(df.options, df.fieldname);
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -75,3 +75,26 @@ $.extend(erpnext.queries, {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
erpnext.queries.setup_queries = function(frm, options, query_fn) {
|
||||
var me = this;
|
||||
var set_query = function(doctype, parentfield) {
|
||||
var link_fields = frappe.meta.get_docfields(doctype, frm.doc.name,
|
||||
{"fieldtype": "Link", "options": options});
|
||||
$.each(link_fields, function(i, df) {
|
||||
if(parentfield) {
|
||||
frm.set_query(df.fieldname, parentfield, query_fn);
|
||||
} else {
|
||||
frm.set_query(df.fieldname, query_fn);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
set_query(frm.doc.doctype);
|
||||
|
||||
// warehouse field in tables
|
||||
$.each(frappe.meta.get_docfields(frm.doc.doctype, frm.doc.name, {"fieldtype": "Table"}),
|
||||
function(i, df) {
|
||||
set_query(df.options, df.fieldname);
|
||||
});
|
||||
}
|
||||
|
@ -3,6 +3,14 @@
|
||||
|
||||
{% include 'selling/sales_common.js' %}
|
||||
|
||||
frappe.ui.form.on("Sales Order", {
|
||||
onload: function(frm) {
|
||||
erpnext.queries.setup_queries(frm, "Warehouse", function() {
|
||||
return erpnext.queries.warehouse(frm.doc);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend({
|
||||
refresh: function(doc, dt, dn) {
|
||||
this._super();
|
||||
@ -16,18 +24,12 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
||||
// cur_frm.dashboard.add_progress(cint(doc.per_billed) + __("% Billed"),
|
||||
// doc.per_billed);
|
||||
|
||||
// delivery note
|
||||
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1)
|
||||
cur_frm.add_custom_button(__('Make Delivery'), this.make_delivery_note);
|
||||
|
||||
// indent
|
||||
if(!doc.order_type || ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1)
|
||||
cur_frm.add_custom_button(__('Make ') + __('Material Request'),
|
||||
this.make_material_request);
|
||||
cur_frm.add_custom_button(__('Material Request'), this.make_material_request);
|
||||
|
||||
// sales invoice
|
||||
if(flt(doc.per_billed, 2) < 100) {
|
||||
cur_frm.add_custom_button(__('Make Invoice'), this.make_sales_invoice);
|
||||
if(flt(doc.per_billed)==0) {
|
||||
cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry);
|
||||
}
|
||||
|
||||
// stop
|
||||
@ -36,8 +38,17 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
||||
|
||||
// maintenance
|
||||
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)===-1) {
|
||||
cur_frm.add_custom_button(__('Make Maint. Visit'), this.make_maintenance_visit);
|
||||
cur_frm.add_custom_button(__('Make Maint. Schedule'), this.make_maintenance_schedule);
|
||||
cur_frm.add_custom_button(__('Maint. Visit'), this.make_maintenance_visit);
|
||||
cur_frm.add_custom_button(__('Maint. Schedule'), this.make_maintenance_schedule);
|
||||
}
|
||||
|
||||
// delivery note
|
||||
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1)
|
||||
cur_frm.add_custom_button(__('Delivery'), this.make_delivery_note).addClass("btn-primary");
|
||||
|
||||
// sales invoice
|
||||
if(flt(doc.per_billed, 2) < 100) {
|
||||
cur_frm.add_custom_button(__('Invoice'), this.make_sales_invoice).addClass("btn-primary");
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -122,6 +133,20 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
||||
frm: cur_frm
|
||||
})
|
||||
},
|
||||
|
||||
make_bank_entry: function() {
|
||||
return frappe.call({
|
||||
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_from_sales_order",
|
||||
args: {
|
||||
"sales_order": cur_frm.doc.name
|
||||
},
|
||||
callback: function(r) {
|
||||
var doclist = frappe.model.sync(r.message);
|
||||
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// for backward compatibility: combine new and previous states
|
||||
|
@ -18,18 +18,18 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
|
||||
});
|
||||
|
||||
if(!from_sales_invoice)
|
||||
cur_frm.add_custom_button(__('Make Invoice'), this.make_sales_invoice);
|
||||
cur_frm.add_custom_button(__('Invoice'), this.make_sales_invoice).addClass("btn-primary");
|
||||
}
|
||||
|
||||
if(flt(doc.per_installed, 2) < 100 && doc.docstatus==1)
|
||||
cur_frm.add_custom_button(__('Make Installation Note'), this.make_installation_note);
|
||||
cur_frm.add_custom_button(__('Installation Note'), this.make_installation_note);
|
||||
|
||||
if (doc.docstatus==1) {
|
||||
cur_frm.add_custom_button(__('Make Sales Return'), this.make_sales_return);
|
||||
cur_frm.add_custom_button(__('Sales Return'), this.make_sales_return);
|
||||
}
|
||||
|
||||
if(doc.docstatus==0 && !doc.__islocal) {
|
||||
cur_frm.add_custom_button(__('Make Packing Slip'),
|
||||
cur_frm.add_custom_button(__('Packing Slip'),
|
||||
cur_frm.cscript['Make Packing Slip'], frappe.boot.doctype_icons["Packing Slip"]);
|
||||
}
|
||||
|
||||
|
@ -52,11 +52,10 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend
|
||||
}
|
||||
|
||||
if(this.frm.doc.docstatus == 1) {
|
||||
cur_frm.add_custom_button(__('Return'), this.make_purchase_return);
|
||||
if(this.frm.doc.__onload && !this.frm.doc.__onload.billing_complete) {
|
||||
cur_frm.add_custom_button(__('Make Purchase Invoice'), this.make_purchase_invoice);
|
||||
cur_frm.add_custom_button(__('Invoice'), this.make_purchase_invoice).addClass("btn-primary");
|
||||
}
|
||||
|
||||
cur_frm.add_custom_button(__('Make Purchase Return'), this.make_purchase_return);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user