mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-25 07:54:46 +00:00
Merge branch 'develop' into fix_by_voucher_order_develop
This commit is contained in:
@@ -9,6 +9,26 @@ frappe.ui.form.on('Accounting Dimension', {
|
|||||||
frappe.set_route("List", frm.doc.document_type);
|
frappe.set_route("List", frm.doc.document_type);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let button = frm.doc.disabled ? "Enable" : "Disable";
|
||||||
|
|
||||||
|
frm.add_custom_button(__(button), function() {
|
||||||
|
|
||||||
|
frm.set_value('disabled', 1 - frm.doc.disabled);
|
||||||
|
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.disable_dimension",
|
||||||
|
args: {
|
||||||
|
doc: frm.doc
|
||||||
|
},
|
||||||
|
freeze: true,
|
||||||
|
callback: function(r) {
|
||||||
|
let message = frm.doc.disabled ? "Dimension Disabled" : "Dimension Enabled";
|
||||||
|
frm.save();
|
||||||
|
frappe.show_alert({message:__(message), indicator:'green'});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
document_type: function(frm) {
|
document_type: function(frm) {
|
||||||
@@ -21,13 +41,4 @@ frappe.ui.form.on('Accounting Dimension', {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
disabled: function(frm) {
|
|
||||||
frappe.call({
|
|
||||||
method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.disable_dimension",
|
|
||||||
args: {
|
|
||||||
doc: frm.doc
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -38,7 +38,8 @@
|
|||||||
"default": "0",
|
"default": "0",
|
||||||
"fieldname": "disabled",
|
"fieldname": "disabled",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Disable"
|
"label": "Disable",
|
||||||
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
@@ -53,7 +54,7 @@
|
|||||||
"label": "Mandatory For Profit and Loss Account"
|
"label": "Mandatory For Profit and Loss Account"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2019-05-27 18:18:17.792726",
|
"modified": "2019-07-07 18:56:19.517450",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Accounting Dimension",
|
"name": "Accounting Dimension",
|
||||||
|
|||||||
@@ -121,11 +121,11 @@ def delete_accounting_dimension(doc):
|
|||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def disable_dimension(doc):
|
def disable_dimension(doc):
|
||||||
if frappe.flags.in_test:
|
if frappe.flags.in_test:
|
||||||
frappe.enqueue(start_dimension_disabling, doc=doc)
|
toggle_disabling(doc=doc)
|
||||||
else:
|
else:
|
||||||
start_dimension_disabling(doc=doc)
|
frappe.enqueue(toggle_disabling, doc=doc)
|
||||||
|
|
||||||
def start_dimension_disabling(doc):
|
def toggle_disabling(doc):
|
||||||
doc = json.loads(doc)
|
doc = json.loads(doc)
|
||||||
|
|
||||||
if doc.get('disabled'):
|
if doc.get('disabled'):
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ class BankReconciliation(Document):
|
|||||||
for d in self.get('payment_entries'):
|
for d in self.get('payment_entries'):
|
||||||
if d.clearance_date:
|
if d.clearance_date:
|
||||||
if not d.payment_document:
|
if not d.payment_document:
|
||||||
frappe.throw(_("Row #{0}: Payment document is required to complete the trasaction"))
|
frappe.throw(_("Row #{0}: Payment document is required to complete the transaction"))
|
||||||
|
|
||||||
if d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date):
|
if d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date):
|
||||||
frappe.throw(_("Row #{0}: Clearance date {1} cannot be before Cheque Date {2}")
|
frappe.throw(_("Row #{0}: Clearance date {1} cannot be before Cheque Date {2}")
|
||||||
@@ -113,10 +113,8 @@ class BankReconciliation(Document):
|
|||||||
if not d.clearance_date:
|
if not d.clearance_date:
|
||||||
d.clearance_date = None
|
d.clearance_date = None
|
||||||
|
|
||||||
frappe.db.set_value(d.payment_document, d.payment_entry, "clearance_date", d.clearance_date)
|
payment_entry = frappe.get_doc(d.payment_document, d.payment_entry)
|
||||||
frappe.db.sql("""update `tab{0}` set clearance_date = %s, modified = %s
|
payment_entry.db_set('clearance_date', d.clearance_date)
|
||||||
where name=%s""".format(d.payment_document),
|
|
||||||
(d.clearance_date, nowdate(), d.payment_entry))
|
|
||||||
|
|
||||||
clearance_date_updated = True
|
clearance_date_updated = True
|
||||||
|
|
||||||
|
|||||||
@@ -21,9 +21,29 @@ frappe.ui.form.on('Exchange Rate Revaluation', {
|
|||||||
|
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
if(frm.doc.docstatus==1) {
|
if(frm.doc.docstatus==1) {
|
||||||
frm.add_custom_button(__('Create Journal Entry'), function() {
|
frappe.db.get_value("Journal Entry Account", {
|
||||||
return frm.events.make_jv(frm);
|
'reference_type': 'Exchange Rate Revaluation',
|
||||||
});
|
'reference_name': frm.doc.name,
|
||||||
|
'docstatus': 1
|
||||||
|
}, "sum(debit) as sum", (r) =>{
|
||||||
|
let total_amt = 0;
|
||||||
|
frm.doc.accounts.forEach(d=> {
|
||||||
|
total_amt = total_amt + d['new_balance_in_base_currency'];
|
||||||
|
});
|
||||||
|
if(total_amt === r.sum) {
|
||||||
|
frm.add_custom_button(__("Journal Entry"), function(){
|
||||||
|
frappe.route_options = {
|
||||||
|
'reference_type': 'Exchange Rate Revaluation',
|
||||||
|
'reference_name': frm.doc.name
|
||||||
|
};
|
||||||
|
frappe.set_route("List", "Journal Entry");
|
||||||
|
}, __("View"));
|
||||||
|
} else {
|
||||||
|
frm.add_custom_button(__('Create Journal Entry'), function() {
|
||||||
|
return frm.events.make_jv(frm);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 'Journal Entry');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -848,6 +848,39 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"translatable": 0,
|
"translatable": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_in_quick_entry": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fetch_if_empty": 0,
|
||||||
|
"fieldname": "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,
|
||||||
|
"translatable": 0,
|
||||||
|
"unique": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"has_web_view": 0,
|
||||||
@@ -861,7 +894,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2019-01-07 07:05:00.366399",
|
"modified": "2019-05-01 07:05:00.366399",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "GL Entry",
|
"name": "GL Entry",
|
||||||
|
|||||||
@@ -228,6 +228,10 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
|||||||
frappe.model.validate_missing(jvd, "account");
|
frappe.model.validate_missing(jvd, "account");
|
||||||
var party_account_field = jvd.reference_type==="Sales Invoice" ? "debit_to": "credit_to";
|
var party_account_field = jvd.reference_type==="Sales Invoice" ? "debit_to": "credit_to";
|
||||||
out.filters.push([jvd.reference_type, party_account_field, "=", jvd.account]);
|
out.filters.push([jvd.reference_type, party_account_field, "=", jvd.account]);
|
||||||
|
|
||||||
|
if (in_list(['Debit Note', 'Credit Note'], doc.voucher_type)) {
|
||||||
|
out.filters.push([jvd.reference_type, "is_return", "=", 1]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(in_list(["Sales Order", "Purchase Order"], jvd.reference_type)) {
|
if(in_list(["Sales Order", "Purchase Order"], jvd.reference_type)) {
|
||||||
|
|||||||
@@ -331,7 +331,8 @@ class JournalEntry(AccountsController):
|
|||||||
for reference_name, total in iteritems(self.reference_totals):
|
for reference_name, total in iteritems(self.reference_totals):
|
||||||
reference_type = self.reference_types[reference_name]
|
reference_type = self.reference_types[reference_name]
|
||||||
|
|
||||||
if reference_type in ("Sales Invoice", "Purchase Invoice"):
|
if (reference_type in ("Sales Invoice", "Purchase Invoice") and
|
||||||
|
self.voucher_type not in ['Debit Note', 'Credit Note']):
|
||||||
invoice = frappe.db.get_value(reference_type, reference_name,
|
invoice = frappe.db.get_value(reference_type, reference_name,
|
||||||
["docstatus", "outstanding_amount"], as_dict=1)
|
["docstatus", "outstanding_amount"], as_dict=1)
|
||||||
|
|
||||||
@@ -497,6 +498,7 @@ class JournalEntry(AccountsController):
|
|||||||
self.get_gl_dict({
|
self.get_gl_dict({
|
||||||
"account": d.account,
|
"account": d.account,
|
||||||
"party_type": d.party_type,
|
"party_type": d.party_type,
|
||||||
|
"due_date": self.due_date,
|
||||||
"party": d.party,
|
"party": d.party,
|
||||||
"against": d.against_account,
|
"against": d.against_account,
|
||||||
"debit": flt(d.debit, d.precision("debit")),
|
"debit": flt(d.debit, d.precision("debit")),
|
||||||
|
|||||||
@@ -302,7 +302,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
},
|
},
|
||||||
() => frm.set_value("party_balance", r.message.party_balance),
|
() => frm.set_value("party_balance", r.message.party_balance),
|
||||||
() => frm.set_value("party_name", r.message.party_name),
|
() => frm.set_value("party_name", r.message.party_name),
|
||||||
() => frm.events.get_outstanding_documents(frm),
|
() => frm.clear_table("references"),
|
||||||
() => frm.events.hide_unhide_fields(frm),
|
() => frm.events.hide_unhide_fields(frm),
|
||||||
() => frm.events.set_dynamic_labels(frm),
|
() => frm.events.set_dynamic_labels(frm),
|
||||||
() => {
|
() => {
|
||||||
@@ -323,9 +323,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
|
|
||||||
frm.events.set_account_currency_and_balance(frm, frm.doc.paid_from,
|
frm.events.set_account_currency_and_balance(frm, frm.doc.paid_from,
|
||||||
"paid_from_account_currency", "paid_from_account_balance", function(frm) {
|
"paid_from_account_currency", "paid_from_account_balance", function(frm) {
|
||||||
if (frm.doc.payment_type == "Receive") {
|
if (frm.doc.payment_type == "Pay") {
|
||||||
frm.events.get_outstanding_documents(frm);
|
|
||||||
} else if (frm.doc.payment_type == "Pay") {
|
|
||||||
frm.events.paid_amount(frm);
|
frm.events.paid_amount(frm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -337,9 +335,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
|
|
||||||
frm.events.set_account_currency_and_balance(frm, frm.doc.paid_to,
|
frm.events.set_account_currency_and_balance(frm, frm.doc.paid_to,
|
||||||
"paid_to_account_currency", "paid_to_account_balance", function(frm) {
|
"paid_to_account_currency", "paid_to_account_balance", function(frm) {
|
||||||
if(frm.doc.payment_type == "Pay") {
|
if (frm.doc.payment_type == "Receive") {
|
||||||
frm.events.get_outstanding_documents(frm);
|
|
||||||
} else if (frm.doc.payment_type == "Receive") {
|
|
||||||
if(frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) {
|
if(frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) {
|
||||||
if(frm.doc.source_exchange_rate) {
|
if(frm.doc.source_exchange_rate) {
|
||||||
frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate);
|
frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate);
|
||||||
@@ -533,26 +529,87 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
frm.events.set_unallocated_amount(frm);
|
frm.events.set_unallocated_amount(frm);
|
||||||
},
|
},
|
||||||
|
|
||||||
get_outstanding_documents: function(frm) {
|
get_outstanding_invoice: function(frm) {
|
||||||
|
const today = frappe.datetime.get_today();
|
||||||
|
const fields = [
|
||||||
|
{fieldtype:"Section Break", label: __("Posting Date")},
|
||||||
|
{fieldtype:"Date", label: __("From Date"),
|
||||||
|
fieldname:"from_posting_date", default:frappe.datetime.add_days(today, -30)},
|
||||||
|
{fieldtype:"Column Break"},
|
||||||
|
{fieldtype:"Date", label: __("To Date"), fieldname:"to_posting_date", default:today},
|
||||||
|
{fieldtype:"Section Break", label: __("Due Date")},
|
||||||
|
{fieldtype:"Date", label: __("From Date"), fieldname:"from_due_date"},
|
||||||
|
{fieldtype:"Column Break"},
|
||||||
|
{fieldtype:"Date", label: __("To Date"), fieldname:"to_due_date"},
|
||||||
|
{fieldtype:"Section Break", label: __("Outstanding Amount")},
|
||||||
|
{fieldtype:"Float", label: __("Greater Than Amount"),
|
||||||
|
fieldname:"outstanding_amt_greater_than", default: 0},
|
||||||
|
{fieldtype:"Column Break"},
|
||||||
|
{fieldtype:"Float", label: __("Less Than Amount"), fieldname:"outstanding_amt_less_than"},
|
||||||
|
{fieldtype:"Section Break"},
|
||||||
|
{fieldtype:"Check", label: __("Allocate Payment Amount"), fieldname:"allocate_payment_amount", default:1},
|
||||||
|
];
|
||||||
|
|
||||||
|
frappe.prompt(fields, function(filters){
|
||||||
|
frappe.flags.allocate_payment_amount = true;
|
||||||
|
frm.events.validate_filters_data(frm, filters);
|
||||||
|
frm.events.get_outstanding_documents(frm, filters);
|
||||||
|
}, __("Filters"), __("Get Outstanding Invoices"));
|
||||||
|
},
|
||||||
|
|
||||||
|
validate_filters_data: function(frm, filters) {
|
||||||
|
const fields = {
|
||||||
|
'Posting Date': ['from_posting_date', 'to_posting_date'],
|
||||||
|
'Due Date': ['from_posting_date', 'to_posting_date'],
|
||||||
|
'Advance Amount': ['from_posting_date', 'to_posting_date'],
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let key in fields) {
|
||||||
|
let from_field = fields[key][0];
|
||||||
|
let to_field = fields[key][1];
|
||||||
|
|
||||||
|
if (filters[from_field] && !filters[to_field]) {
|
||||||
|
frappe.throw(__("Error: {0} is mandatory field",
|
||||||
|
[to_field.replace(/_/g, " ")]
|
||||||
|
));
|
||||||
|
} else if (filters[from_field] && filters[from_field] > filters[to_field]) {
|
||||||
|
frappe.throw(__("{0}: {1} must be less than {2}",
|
||||||
|
[key, from_field.replace(/_/g, " "), to_field.replace(/_/g, " ")]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
get_outstanding_documents: function(frm, filters) {
|
||||||
frm.clear_table("references");
|
frm.clear_table("references");
|
||||||
|
|
||||||
if(!frm.doc.party) return;
|
if(!frm.doc.party) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
frm.events.check_mandatory_to_fetch(frm);
|
frm.events.check_mandatory_to_fetch(frm);
|
||||||
var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
|
var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
|
||||||
|
|
||||||
|
var args = {
|
||||||
|
"posting_date": frm.doc.posting_date,
|
||||||
|
"company": frm.doc.company,
|
||||||
|
"party_type": frm.doc.party_type,
|
||||||
|
"payment_type": frm.doc.payment_type,
|
||||||
|
"party": frm.doc.party,
|
||||||
|
"party_account": frm.doc.payment_type=="Receive" ? frm.doc.paid_from : frm.doc.paid_to,
|
||||||
|
"cost_center": frm.doc.cost_center
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let key in filters) {
|
||||||
|
args[key] = filters[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
frappe.flags.allocate_payment_amount = filters['allocate_payment_amount'];
|
||||||
|
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
method: 'erpnext.accounts.doctype.payment_entry.payment_entry.get_outstanding_reference_documents',
|
method: 'erpnext.accounts.doctype.payment_entry.payment_entry.get_outstanding_reference_documents',
|
||||||
args: {
|
args: {
|
||||||
args: {
|
args:args
|
||||||
"posting_date": frm.doc.posting_date,
|
|
||||||
"company": frm.doc.company,
|
|
||||||
"party_type": frm.doc.party_type,
|
|
||||||
"payment_type": frm.doc.payment_type,
|
|
||||||
"party": frm.doc.party,
|
|
||||||
"party_account": frm.doc.payment_type=="Receive" ? frm.doc.paid_from : frm.doc.paid_to,
|
|
||||||
"cost_center": frm.doc.cost_center
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
callback: function(r, rt) {
|
callback: function(r, rt) {
|
||||||
if(r.message) {
|
if(r.message) {
|
||||||
@@ -608,25 +665,11 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
|
|
||||||
frm.events.allocate_party_amount_against_ref_docs(frm,
|
frm.events.allocate_party_amount_against_ref_docs(frm,
|
||||||
(frm.doc.payment_type=="Receive" ? frm.doc.paid_amount : frm.doc.received_amount));
|
(frm.doc.payment_type=="Receive" ? frm.doc.paid_amount : frm.doc.received_amount));
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
allocate_payment_amount: function(frm) {
|
|
||||||
if(frm.doc.payment_type == 'Internal Transfer'){
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if(frm.doc.references.length == 0){
|
|
||||||
frm.events.get_outstanding_documents(frm);
|
|
||||||
}
|
|
||||||
if(frm.doc.payment_type == 'Internal Transfer') {
|
|
||||||
frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.paid_amount);
|
|
||||||
} else {
|
|
||||||
frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.received_amount);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
allocate_party_amount_against_ref_docs: function(frm, paid_amount) {
|
allocate_party_amount_against_ref_docs: function(frm, paid_amount) {
|
||||||
var total_positive_outstanding_including_order = 0;
|
var total_positive_outstanding_including_order = 0;
|
||||||
var total_negative_outstanding = 0;
|
var total_negative_outstanding = 0;
|
||||||
@@ -677,7 +720,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
|
|
||||||
$.each(frm.doc.references || [], function(i, row) {
|
$.each(frm.doc.references || [], function(i, row) {
|
||||||
row.allocated_amount = 0 //If allocate payment amount checkbox is unchecked, set zero to allocate amount
|
row.allocated_amount = 0 //If allocate payment amount checkbox is unchecked, set zero to allocate amount
|
||||||
if(frm.doc.allocate_payment_amount){
|
if(frappe.flags.allocate_payment_amount){
|
||||||
if(row.outstanding_amount > 0 && allocated_positive_outstanding > 0) {
|
if(row.outstanding_amount > 0 && allocated_positive_outstanding > 0) {
|
||||||
if(row.outstanding_amount >= allocated_positive_outstanding) {
|
if(row.outstanding_amount >= allocated_positive_outstanding) {
|
||||||
row.allocated_amount = allocated_positive_outstanding;
|
row.allocated_amount = allocated_positive_outstanding;
|
||||||
@@ -958,7 +1001,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
if(frm.doc.payment_type != "Internal") {
|
if(frm.doc.payment_type != "Internal") {
|
||||||
frm.events.get_outstanding_documents(frm);
|
frm.clear_table("references");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
"target_exchange_rate",
|
"target_exchange_rate",
|
||||||
"base_received_amount",
|
"base_received_amount",
|
||||||
"section_break_14",
|
"section_break_14",
|
||||||
"allocate_payment_amount",
|
"get_outstanding_invoice",
|
||||||
"references",
|
"references",
|
||||||
"section_break_34",
|
"section_break_34",
|
||||||
"total_allocated_amount",
|
"total_allocated_amount",
|
||||||
@@ -325,19 +325,15 @@
|
|||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"collapsible": 1,
|
|
||||||
"collapsible_depends_on": "references",
|
|
||||||
"depends_on": "eval:(doc.party && doc.paid_from && doc.paid_to && doc.paid_amount && doc.received_amount)",
|
"depends_on": "eval:(doc.party && doc.paid_from && doc.paid_to && doc.paid_amount && doc.received_amount)",
|
||||||
"fieldname": "section_break_14",
|
"fieldname": "section_break_14",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Reference"
|
"label": "Reference"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "1",
|
"fieldname": "get_outstanding_invoice",
|
||||||
"depends_on": "eval:in_list(['Pay', 'Receive'], doc.payment_type)",
|
"fieldtype": "Button",
|
||||||
"fieldname": "allocate_payment_amount",
|
"label": "Get Outstanding Invoice"
|
||||||
"fieldtype": "Check",
|
|
||||||
"label": "Allocate Payment Amount"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "references",
|
"fieldname": "references",
|
||||||
@@ -570,7 +566,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2019-05-25 22:02:40.575822",
|
"modified": "2019-05-27 15:53:21.108857",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Entry",
|
"name": "Payment Entry",
|
||||||
|
|||||||
@@ -535,6 +535,20 @@ class PaymentEntry(AccountsController):
|
|||||||
"amount": self.total_allocated_amount * (tax_details['tax']['rate'] / 100)
|
"amount": self.total_allocated_amount * (tax_details['tax']['rate'] / 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def set_gain_or_loss(self, account_details=None):
|
||||||
|
if not self.difference_amount:
|
||||||
|
self.set_difference_amount()
|
||||||
|
|
||||||
|
row = {
|
||||||
|
'amount': self.difference_amount
|
||||||
|
}
|
||||||
|
|
||||||
|
if account_details:
|
||||||
|
row.update(account_details)
|
||||||
|
|
||||||
|
self.append('deductions', row)
|
||||||
|
self.set_unallocated_amount()
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_outstanding_reference_documents(args):
|
def get_outstanding_reference_documents(args):
|
||||||
|
|
||||||
@@ -560,8 +574,8 @@ def get_outstanding_reference_documents(args):
|
|||||||
# Get negative outstanding sales /purchase invoices
|
# Get negative outstanding sales /purchase invoices
|
||||||
negative_outstanding_invoices = []
|
negative_outstanding_invoices = []
|
||||||
if args.get("party_type") not in ["Student", "Employee"] and not args.get("voucher_no"):
|
if args.get("party_type") not in ["Student", "Employee"] and not args.get("voucher_no"):
|
||||||
negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"),
|
negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), args.get("party"),
|
||||||
args.get("party"), args.get("party_account"), party_account_currency, company_currency)
|
args.get("party_account"), args.get("company"), party_account_currency, company_currency)
|
||||||
|
|
||||||
# Get positive outstanding sales /purchase invoices/ Fees
|
# Get positive outstanding sales /purchase invoices/ Fees
|
||||||
condition = ""
|
condition = ""
|
||||||
@@ -571,10 +585,23 @@ def get_outstanding_reference_documents(args):
|
|||||||
|
|
||||||
# Add cost center condition
|
# Add cost center condition
|
||||||
if args.get("cost_center") and get_allow_cost_center_in_entry_of_bs_account():
|
if args.get("cost_center") and get_allow_cost_center_in_entry_of_bs_account():
|
||||||
condition += " and cost_center='%s'" % args.get("cost_center")
|
condition += " and cost_center='%s'" % args.get("cost_center")
|
||||||
|
|
||||||
|
date_fields_dict = {
|
||||||
|
'posting_date': ['from_posting_date', 'to_posting_date'],
|
||||||
|
'due_date': ['from_due_date', 'to_due_date']
|
||||||
|
}
|
||||||
|
|
||||||
|
for fieldname, date_fields in date_fields_dict.items():
|
||||||
|
if args.get(date_fields[0]) and args.get(date_fields[1]):
|
||||||
|
condition += " and {0} between '{1}' and '{2}'".format(fieldname,
|
||||||
|
args.get(date_fields[0]), args.get(date_fields[1]))
|
||||||
|
|
||||||
|
if args.get("company"):
|
||||||
|
condition += " and company = {0}".format(frappe.db.escape(args.get("company")))
|
||||||
|
|
||||||
outstanding_invoices = get_outstanding_invoices(args.get("party_type"), args.get("party"),
|
outstanding_invoices = get_outstanding_invoices(args.get("party_type"), args.get("party"),
|
||||||
args.get("party_account"), condition=condition)
|
args.get("party_account"), filters=args, condition=condition, limit=100)
|
||||||
|
|
||||||
for d in outstanding_invoices:
|
for d in outstanding_invoices:
|
||||||
d["exchange_rate"] = 1
|
d["exchange_rate"] = 1
|
||||||
@@ -592,12 +619,19 @@ def get_outstanding_reference_documents(args):
|
|||||||
orders_to_be_billed = []
|
orders_to_be_billed = []
|
||||||
if (args.get("party_type") != "Student"):
|
if (args.get("party_type") != "Student"):
|
||||||
orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"),
|
orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"),
|
||||||
args.get("party"), party_account_currency, company_currency)
|
args.get("party"), args.get("company"), party_account_currency, company_currency, filters=args)
|
||||||
|
|
||||||
return negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed
|
data = negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
frappe.msgprint(_("No outstanding invoices found for the {0} <b>{1}</b>.")
|
||||||
|
.format(args.get("party_type").lower(), args.get("party")))
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
def get_orders_to_be_billed(posting_date, party_type, party, party_account_currency, company_currency, cost_center=None):
|
def get_orders_to_be_billed(posting_date, party_type, party,
|
||||||
|
company, party_account_currency, company_currency, cost_center=None, filters=None):
|
||||||
if party_type == "Customer":
|
if party_type == "Customer":
|
||||||
voucher_type = 'Sales Order'
|
voucher_type = 'Sales Order'
|
||||||
elif party_type == "Supplier":
|
elif party_type == "Supplier":
|
||||||
@@ -627,6 +661,7 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre
|
|||||||
where
|
where
|
||||||
{party_type} = %s
|
{party_type} = %s
|
||||||
and docstatus = 1
|
and docstatus = 1
|
||||||
|
and company = %s
|
||||||
and ifnull(status, "") != "Closed"
|
and ifnull(status, "") != "Closed"
|
||||||
and {ref_field} > advance_paid
|
and {ref_field} > advance_paid
|
||||||
and abs(100 - per_billed) > 0.01
|
and abs(100 - per_billed) > 0.01
|
||||||
@@ -638,10 +673,14 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre
|
|||||||
"voucher_type": voucher_type,
|
"voucher_type": voucher_type,
|
||||||
"party_type": scrub(party_type),
|
"party_type": scrub(party_type),
|
||||||
"condition": condition
|
"condition": condition
|
||||||
}), party, as_dict=True)
|
}), (party, company), as_dict=True)
|
||||||
|
|
||||||
order_list = []
|
order_list = []
|
||||||
for d in orders:
|
for d in orders:
|
||||||
|
if not (d.outstanding_amount >= filters.get("outstanding_amt_greater_than")
|
||||||
|
and d.outstanding_amount <= filters.get("outstanding_amt_less_than")):
|
||||||
|
continue
|
||||||
|
|
||||||
d["voucher_type"] = voucher_type
|
d["voucher_type"] = voucher_type
|
||||||
# This assumes that the exchange rate required is the one in the SO
|
# This assumes that the exchange rate required is the one in the SO
|
||||||
d["exchange_rate"] = get_exchange_rate(party_account_currency, company_currency, posting_date)
|
d["exchange_rate"] = get_exchange_rate(party_account_currency, company_currency, posting_date)
|
||||||
@@ -649,7 +688,8 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre
|
|||||||
|
|
||||||
return order_list
|
return order_list
|
||||||
|
|
||||||
def get_negative_outstanding_invoices(party_type, party, party_account, party_account_currency, company_currency, cost_center=None):
|
def get_negative_outstanding_invoices(party_type, party, party_account,
|
||||||
|
company, party_account_currency, company_currency, cost_center=None):
|
||||||
voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"
|
voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"
|
||||||
supplier_condition = ""
|
supplier_condition = ""
|
||||||
if voucher_type == "Purchase Invoice":
|
if voucher_type == "Purchase Invoice":
|
||||||
@@ -670,7 +710,8 @@ def get_negative_outstanding_invoices(party_type, party, party_account, party_ac
|
|||||||
from
|
from
|
||||||
`tab{voucher_type}`
|
`tab{voucher_type}`
|
||||||
where
|
where
|
||||||
{party_type} = %s and {party_account} = %s and docstatus = 1 and outstanding_amount < 0
|
{party_type} = %s and {party_account} = %s and docstatus = 1 and
|
||||||
|
company = %s and outstanding_amount < 0
|
||||||
{supplier_condition}
|
{supplier_condition}
|
||||||
order by
|
order by
|
||||||
posting_date, name
|
posting_date, name
|
||||||
@@ -682,7 +723,7 @@ def get_negative_outstanding_invoices(party_type, party, party_account, party_ac
|
|||||||
"party_type": scrub(party_type),
|
"party_type": scrub(party_type),
|
||||||
"party_account": "debit_to" if party_type == "Customer" else "credit_to",
|
"party_account": "debit_to" if party_type == "Customer" else "credit_to",
|
||||||
"cost_center": cost_center
|
"cost_center": cost_center
|
||||||
}), (party, party_account), as_dict=True)
|
}), (party, party_account, company), as_dict=True)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@@ -910,7 +951,6 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
|||||||
pe.paid_to_account_currency = party_account_currency if payment_type=="Pay" else bank.account_currency
|
pe.paid_to_account_currency = party_account_currency if payment_type=="Pay" else bank.account_currency
|
||||||
pe.paid_amount = paid_amount
|
pe.paid_amount = paid_amount
|
||||||
pe.received_amount = received_amount
|
pe.received_amount = received_amount
|
||||||
pe.allocate_payment_amount = 1
|
|
||||||
pe.letter_head = doc.get("letter_head")
|
pe.letter_head = doc.get("letter_head")
|
||||||
|
|
||||||
if pe.party_type in ["Customer", "Supplier"]:
|
if pe.party_type in ["Customer", "Supplier"]:
|
||||||
|
|||||||
@@ -16,6 +16,20 @@ frappe.ui.form.on("Payment Reconciliation Payment", {
|
|||||||
})[0].outstanding_amount;
|
})[0].outstanding_amount;
|
||||||
|
|
||||||
frappe.model.set_value(cdt, cdn, "allocated_amount", Math.min(invoice_amount, row.amount));
|
frappe.model.set_value(cdt, cdn, "allocated_amount", Math.min(invoice_amount, row.amount));
|
||||||
|
|
||||||
|
frm.call({
|
||||||
|
doc: frm.doc,
|
||||||
|
method: 'get_difference_amount',
|
||||||
|
args: {
|
||||||
|
child_row: row
|
||||||
|
},
|
||||||
|
callback: function(r, rt) {
|
||||||
|
if(r.message) {
|
||||||
|
frappe.model.set_value(cdt, cdn,
|
||||||
|
"difference_amount", r.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -104,6 +118,91 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
|||||||
|
|
||||||
reconcile: function() {
|
reconcile: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
|
var show_dialog = me.frm.doc.payments.filter(d => d.difference_amount && !d.difference_account);
|
||||||
|
|
||||||
|
if (show_dialog && show_dialog.length) {
|
||||||
|
|
||||||
|
this.data = [];
|
||||||
|
const dialog = new frappe.ui.Dialog({
|
||||||
|
title: __("Select Difference Account"),
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
fieldname: "payments", fieldtype: "Table", label: __("Payments"),
|
||||||
|
data: this.data, in_place_edit: true,
|
||||||
|
get_data: () => {
|
||||||
|
return this.data;
|
||||||
|
},
|
||||||
|
fields: [{
|
||||||
|
fieldtype:'Data',
|
||||||
|
fieldname:"docname",
|
||||||
|
in_list_view: 1,
|
||||||
|
hidden: 1
|
||||||
|
}, {
|
||||||
|
fieldtype:'Data',
|
||||||
|
fieldname:"reference_name",
|
||||||
|
label: __("Voucher No"),
|
||||||
|
in_list_view: 1,
|
||||||
|
read_only: 1
|
||||||
|
}, {
|
||||||
|
fieldtype:'Link',
|
||||||
|
options: 'Account',
|
||||||
|
in_list_view: 1,
|
||||||
|
label: __("Difference Account"),
|
||||||
|
fieldname: 'difference_account',
|
||||||
|
reqd: 1,
|
||||||
|
get_query: function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
company: me.frm.doc.company,
|
||||||
|
is_group: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
fieldtype:'Currency',
|
||||||
|
in_list_view: 1,
|
||||||
|
label: __("Difference Amount"),
|
||||||
|
fieldname: 'difference_amount',
|
||||||
|
read_only: 1
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
],
|
||||||
|
primary_action: function() {
|
||||||
|
const args = dialog.get_values()["payments"];
|
||||||
|
|
||||||
|
args.forEach(d => {
|
||||||
|
frappe.model.set_value("Payment Reconciliation Payment", d.docname,
|
||||||
|
"difference_account", d.difference_account);
|
||||||
|
});
|
||||||
|
|
||||||
|
me.reconcile_payment_entries();
|
||||||
|
dialog.hide();
|
||||||
|
},
|
||||||
|
primary_action_label: __('Reconcile Entries')
|
||||||
|
});
|
||||||
|
|
||||||
|
this.frm.doc.payments.forEach(d => {
|
||||||
|
if (d.difference_amount && !d.difference_account) {
|
||||||
|
dialog.fields_dict.payments.df.data.push({
|
||||||
|
'docname': d.name,
|
||||||
|
'reference_name': d.reference_name,
|
||||||
|
'difference_amount': d.difference_amount,
|
||||||
|
'difference_account': d.difference_account,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.data = dialog.fields_dict.payments.df.data;
|
||||||
|
dialog.fields_dict.payments.grid.refresh();
|
||||||
|
dialog.show();
|
||||||
|
} else {
|
||||||
|
this.reconcile_payment_entries();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
reconcile_payment_entries: function() {
|
||||||
|
var me = this;
|
||||||
|
|
||||||
return this.frm.call({
|
return this.frm.call({
|
||||||
doc: me.frm.doc,
|
doc: me.frm.doc,
|
||||||
method: 'reconcile',
|
method: 'reconcile',
|
||||||
|
|||||||
@@ -3,10 +3,11 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe, erpnext
|
import frappe, erpnext
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt, today
|
||||||
from frappe import msgprint, _
|
from frappe import msgprint, _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from erpnext.accounts.utils import get_outstanding_invoices
|
from erpnext.accounts.utils import (get_outstanding_invoices,
|
||||||
|
update_reference_in_payment_entry, reconcile_against_document)
|
||||||
from erpnext.controllers.accounts_controller import get_advance_payment_entries
|
from erpnext.controllers.accounts_controller import get_advance_payment_entries
|
||||||
|
|
||||||
class PaymentReconciliation(Document):
|
class PaymentReconciliation(Document):
|
||||||
@@ -20,7 +21,10 @@ class PaymentReconciliation(Document):
|
|||||||
payment_entries = self.get_payment_entries()
|
payment_entries = self.get_payment_entries()
|
||||||
journal_entries = self.get_jv_entries()
|
journal_entries = self.get_jv_entries()
|
||||||
|
|
||||||
self.add_payment_entries(payment_entries + journal_entries)
|
if self.party_type in ["Customer", "Supplier"]:
|
||||||
|
dr_or_cr_notes = self.get_dr_or_cr_notes()
|
||||||
|
|
||||||
|
self.add_payment_entries(payment_entries + journal_entries + dr_or_cr_notes)
|
||||||
|
|
||||||
def get_payment_entries(self):
|
def get_payment_entries(self):
|
||||||
order_doctype = "Sales Order" if self.party_type=="Customer" else "Purchase Order"
|
order_doctype = "Sales Order" if self.party_type=="Customer" else "Purchase Order"
|
||||||
@@ -71,6 +75,34 @@ class PaymentReconciliation(Document):
|
|||||||
|
|
||||||
return list(journal_entries)
|
return list(journal_entries)
|
||||||
|
|
||||||
|
def get_dr_or_cr_notes(self):
|
||||||
|
dr_or_cr = ("credit_in_account_currency"
|
||||||
|
if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit_in_account_currency")
|
||||||
|
|
||||||
|
reconciled_dr_or_cr = ("debit_in_account_currency"
|
||||||
|
if dr_or_cr == "credit_in_account_currency" else "credit_in_account_currency")
|
||||||
|
|
||||||
|
voucher_type = ('Sales Invoice'
|
||||||
|
if self.party_type == 'Customer' else "Purchase Invoice")
|
||||||
|
|
||||||
|
return frappe.db.sql(""" SELECT `tab{doc}`.name as reference_name, %(voucher_type)s as reference_type,
|
||||||
|
(sum(`tabGL Entry`.{dr_or_cr}) - sum(`tabGL Entry`.{reconciled_dr_or_cr})) as amount
|
||||||
|
FROM `tab{doc}`, `tabGL Entry`
|
||||||
|
WHERE
|
||||||
|
(`tab{doc}`.name = `tabGL Entry`.against_voucher or `tab{doc}`.name = `tabGL Entry`.voucher_no)
|
||||||
|
and `tab{doc}`.is_return = 1 and `tabGL Entry`.against_voucher_type = %(voucher_type)s
|
||||||
|
and `tab{doc}`.docstatus = 1 and `tabGL Entry`.party = %(party)s
|
||||||
|
and `tabGL Entry`.party_type = %(party_type)s and `tabGL Entry`.account = %(account)s
|
||||||
|
GROUP BY `tabSales Invoice`.name
|
||||||
|
Having
|
||||||
|
amount > 0
|
||||||
|
""".format(doc=voucher_type, dr_or_cr=dr_or_cr, reconciled_dr_or_cr=reconciled_dr_or_cr), {
|
||||||
|
'party': self.party,
|
||||||
|
'party_type': self.party_type,
|
||||||
|
'voucher_type': voucher_type,
|
||||||
|
'account': self.receivable_payable_account
|
||||||
|
}, as_dict=1)
|
||||||
|
|
||||||
def add_payment_entries(self, entries):
|
def add_payment_entries(self, entries):
|
||||||
self.set('payments', [])
|
self.set('payments', [])
|
||||||
for e in entries:
|
for e in entries:
|
||||||
@@ -114,36 +146,67 @@ class PaymentReconciliation(Document):
|
|||||||
if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit_in_account_currency")
|
if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit_in_account_currency")
|
||||||
|
|
||||||
lst = []
|
lst = []
|
||||||
|
dr_or_cr_notes = []
|
||||||
for e in self.get('payments'):
|
for e in self.get('payments'):
|
||||||
|
reconciled_entry = []
|
||||||
if e.invoice_number and e.allocated_amount:
|
if e.invoice_number and e.allocated_amount:
|
||||||
lst.append(frappe._dict({
|
if e.reference_type in ['Sales Invoice', 'Purchase Invoice']:
|
||||||
'voucher_type': e.reference_type,
|
reconciled_entry = dr_or_cr_notes
|
||||||
'voucher_no' : e.reference_name,
|
else:
|
||||||
'voucher_detail_no' : e.reference_row,
|
reconciled_entry = lst
|
||||||
'against_voucher_type' : e.invoice_type,
|
|
||||||
'against_voucher' : e.invoice_number,
|
reconciled_entry.append(self.get_payment_details(e, dr_or_cr))
|
||||||
'account' : self.receivable_payable_account,
|
|
||||||
'party_type': self.party_type,
|
|
||||||
'party': self.party,
|
|
||||||
'is_advance' : e.is_advance,
|
|
||||||
'dr_or_cr' : dr_or_cr,
|
|
||||||
'unadjusted_amount' : flt(e.amount),
|
|
||||||
'allocated_amount' : flt(e.allocated_amount)
|
|
||||||
}))
|
|
||||||
|
|
||||||
if lst:
|
if lst:
|
||||||
from erpnext.accounts.utils import reconcile_against_document
|
|
||||||
reconcile_against_document(lst)
|
reconcile_against_document(lst)
|
||||||
|
|
||||||
msgprint(_("Successfully Reconciled"))
|
if dr_or_cr_notes:
|
||||||
self.get_unreconciled_entries()
|
reconcile_dr_cr_note(dr_or_cr_notes)
|
||||||
|
|
||||||
|
msgprint(_("Successfully Reconciled"))
|
||||||
|
self.get_unreconciled_entries()
|
||||||
|
|
||||||
|
def get_payment_details(self, row, dr_or_cr):
|
||||||
|
return frappe._dict({
|
||||||
|
'voucher_type': row.reference_type,
|
||||||
|
'voucher_no' : row.reference_name,
|
||||||
|
'voucher_detail_no' : row.reference_row,
|
||||||
|
'against_voucher_type' : row.invoice_type,
|
||||||
|
'against_voucher' : row.invoice_number,
|
||||||
|
'account' : self.receivable_payable_account,
|
||||||
|
'party_type': self.party_type,
|
||||||
|
'party': self.party,
|
||||||
|
'is_advance' : row.is_advance,
|
||||||
|
'dr_or_cr' : dr_or_cr,
|
||||||
|
'unadjusted_amount' : flt(row.amount),
|
||||||
|
'allocated_amount' : flt(row.allocated_amount),
|
||||||
|
'difference_amount': row.difference_amount,
|
||||||
|
'difference_account': row.difference_account
|
||||||
|
})
|
||||||
|
|
||||||
|
def get_difference_amount(self, child_row):
|
||||||
|
if child_row.get("reference_type") != 'Payment Entry': return
|
||||||
|
|
||||||
|
child_row = frappe._dict(child_row)
|
||||||
|
|
||||||
|
if child_row.invoice_number and " | " in child_row.invoice_number:
|
||||||
|
child_row.invoice_type, child_row.invoice_number = child_row.invoice_number.split(" | ")
|
||||||
|
|
||||||
|
dr_or_cr = ("credit_in_account_currency"
|
||||||
|
if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit_in_account_currency")
|
||||||
|
|
||||||
|
row = self.get_payment_details(child_row, dr_or_cr)
|
||||||
|
|
||||||
|
doc = frappe.get_doc(row.voucher_type, row.voucher_no)
|
||||||
|
update_reference_in_payment_entry(row, doc, do_not_save=True)
|
||||||
|
|
||||||
|
return doc.difference_amount
|
||||||
|
|
||||||
def check_mandatory_to_fetch(self):
|
def check_mandatory_to_fetch(self):
|
||||||
for fieldname in ["company", "party_type", "party", "receivable_payable_account"]:
|
for fieldname in ["company", "party_type", "party", "receivable_payable_account"]:
|
||||||
if not self.get(fieldname):
|
if not self.get(fieldname):
|
||||||
frappe.throw(_("Please select {0} first").format(self.meta.get_label(fieldname)))
|
frappe.throw(_("Please select {0} first").format(self.meta.get_label(fieldname)))
|
||||||
|
|
||||||
|
|
||||||
def validate_invoice(self):
|
def validate_invoice(self):
|
||||||
if not self.get("invoices"):
|
if not self.get("invoices"):
|
||||||
frappe.throw(_("No records found in the Invoice table"))
|
frappe.throw(_("No records found in the Invoice table"))
|
||||||
@@ -188,3 +251,41 @@ class PaymentReconciliation(Document):
|
|||||||
cond += " and `{0}` <= {1}".format(dr_or_cr, flt(self.maximum_amount))
|
cond += " and `{0}` <= {1}".format(dr_or_cr, flt(self.maximum_amount))
|
||||||
|
|
||||||
return cond
|
return cond
|
||||||
|
|
||||||
|
def reconcile_dr_cr_note(dr_cr_notes):
|
||||||
|
for d in dr_cr_notes:
|
||||||
|
voucher_type = ('Credit Note'
|
||||||
|
if d.voucher_type == 'Sales Invoice' else 'Debit Note')
|
||||||
|
|
||||||
|
dr_or_cr = ('credit_in_account_currency'
|
||||||
|
if d.reference_type == 'Sales Invoice' else 'debit_in_account_currency')
|
||||||
|
|
||||||
|
reconcile_dr_or_cr = ('debit_in_account_currency'
|
||||||
|
if dr_or_cr == 'credit_in_account_currency' else 'credit_in_account_currency')
|
||||||
|
|
||||||
|
jv = frappe.get_doc({
|
||||||
|
"doctype": "Journal Entry",
|
||||||
|
"voucher_type": voucher_type,
|
||||||
|
"posting_date": today(),
|
||||||
|
"accounts": [
|
||||||
|
{
|
||||||
|
'account': d.account,
|
||||||
|
'party': d.party,
|
||||||
|
'party_type': d.party_type,
|
||||||
|
reconcile_dr_or_cr: (abs(d.allocated_amount)
|
||||||
|
if abs(d.unadjusted_amount) > abs(d.allocated_amount) else abs(d.unadjusted_amount)),
|
||||||
|
'reference_type': d.against_voucher_type,
|
||||||
|
'reference_name': d.against_voucher
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'account': d.account,
|
||||||
|
'party': d.party,
|
||||||
|
'party_type': d.party_type,
|
||||||
|
dr_or_cr: abs(d.allocated_amount),
|
||||||
|
'reference_type': d.voucher_type,
|
||||||
|
'reference_name': d.voucher_no
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
jv.submit()
|
||||||
@@ -1,389 +1,127 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"creation": "2014-07-09 16:13:35.452759",
|
||||||
"allow_events_in_timeline": 0,
|
"doctype": "DocType",
|
||||||
"allow_guest_to_view": 0,
|
"editable_grid": 1,
|
||||||
"allow_import": 0,
|
"field_order": [
|
||||||
"allow_rename": 0,
|
"reference_type",
|
||||||
"beta": 0,
|
"reference_name",
|
||||||
"creation": "2014-07-09 16:13:35.452759",
|
"posting_date",
|
||||||
"custom": 0,
|
"is_advance",
|
||||||
"docstatus": 0,
|
"reference_row",
|
||||||
"doctype": "DocType",
|
"col_break1",
|
||||||
"document_type": "",
|
"invoice_number",
|
||||||
"editable_grid": 1,
|
"amount",
|
||||||
|
"allocated_amount",
|
||||||
|
"section_break_10",
|
||||||
|
"difference_account",
|
||||||
|
"difference_amount",
|
||||||
|
"sec_break1",
|
||||||
|
"remark"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "reference_type",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Link",
|
||||||
"allow_on_submit": 0,
|
"label": "Reference Type",
|
||||||
"bold": 0,
|
"options": "DocType",
|
||||||
"collapsible": 0,
|
"read_only": 1
|
||||||
"columns": 0,
|
},
|
||||||
"fieldname": "reference_type",
|
|
||||||
"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": "Reference Type",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "DocType",
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"columns": 2,
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "reference_name",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Dynamic Link",
|
||||||
"bold": 0,
|
"in_list_view": 1,
|
||||||
"collapsible": 0,
|
"label": "Reference Name",
|
||||||
"columns": 3,
|
"options": "reference_type",
|
||||||
"fieldname": "reference_name",
|
"read_only": 1
|
||||||
"fieldtype": "Dynamic 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": "Reference Name",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "reference_type",
|
|
||||||
"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,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "posting_date",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Date",
|
||||||
"allow_on_submit": 0,
|
"label": "Posting Date",
|
||||||
"bold": 0,
|
"read_only": 1
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "posting_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": "Posting Date",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "is_advance",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Data",
|
||||||
"allow_on_submit": 0,
|
"hidden": 1,
|
||||||
"bold": 0,
|
"label": "Is Advance",
|
||||||
"collapsible": 0,
|
"read_only": 1
|
||||||
"columns": 0,
|
},
|
||||||
"fieldname": "is_advance",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"hidden": 1,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Is Advance",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "reference_row",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Data",
|
||||||
"allow_on_submit": 0,
|
"hidden": 1,
|
||||||
"bold": 0,
|
"label": "Reference Row",
|
||||||
"collapsible": 0,
|
"read_only": 1
|
||||||
"columns": 0,
|
},
|
||||||
"fieldname": "reference_row",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"hidden": 1,
|
|
||||||
"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 Row",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "col_break1",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Column Break"
|
||||||
"allow_on_submit": 0,
|
},
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "col_break1",
|
|
||||||
"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,
|
|
||||||
"label": "",
|
|
||||||
"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,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"columns": 2,
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "invoice_number",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Select",
|
||||||
"bold": 0,
|
"in_list_view": 1,
|
||||||
"collapsible": 0,
|
"label": "Invoice Number",
|
||||||
"columns": 3,
|
"reqd": 1
|
||||||
"fieldname": "invoice_number",
|
},
|
||||||
"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": "Invoice Number",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"columns": 2,
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "amount",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Currency",
|
||||||
"bold": 0,
|
"in_list_view": 1,
|
||||||
"collapsible": 0,
|
"label": "Amount",
|
||||||
"columns": 2,
|
"read_only": 1
|
||||||
"fieldname": "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": "Amount",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"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,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"columns": 2,
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "allocated_amount",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Currency",
|
||||||
"bold": 0,
|
"in_list_view": 1,
|
||||||
"collapsible": 0,
|
"label": "Allocated amount",
|
||||||
"columns": 2,
|
"reqd": 1
|
||||||
"fieldname": "allocated_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": "Allocated amount",
|
|
||||||
"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": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "sec_break1",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Section Break"
|
||||||
"allow_on_submit": 0,
|
},
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "sec_break1",
|
|
||||||
"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": "",
|
|
||||||
"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,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "remark",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldtype": "Small Text",
|
||||||
"allow_on_submit": 0,
|
"in_list_view": 1,
|
||||||
"bold": 0,
|
"label": "Remark",
|
||||||
"collapsible": 0,
|
"read_only": 1
|
||||||
"columns": 0,
|
},
|
||||||
"fieldname": "remark",
|
{
|
||||||
"fieldtype": "Small Text",
|
"columns": 2,
|
||||||
"hidden": 0,
|
"fieldname": "difference_account",
|
||||||
"ignore_user_permissions": 0,
|
"fieldtype": "Link",
|
||||||
"ignore_xss_filter": 0,
|
"in_list_view": 1,
|
||||||
"in_filter": 0,
|
"label": "Difference Account",
|
||||||
"in_global_search": 0,
|
"options": "Account"
|
||||||
"in_list_view": 1,
|
},
|
||||||
"in_standard_filter": 0,
|
{
|
||||||
"label": "Remark",
|
"fieldname": "difference_amount",
|
||||||
"length": 0,
|
"fieldtype": "Currency",
|
||||||
"no_copy": 0,
|
"label": "Difference Amount",
|
||||||
"permlevel": 0,
|
"print_hide": 1,
|
||||||
"print_hide": 0,
|
"read_only": 1
|
||||||
"print_hide_if_no_value": 0,
|
},
|
||||||
"read_only": 1,
|
{
|
||||||
"remember_last_selected_value": 0,
|
"fieldname": "section_break_10",
|
||||||
"report_hide": 0,
|
"fieldtype": "Section Break"
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"istable": 1,
|
||||||
"hide_heading": 0,
|
"modified": "2019-06-24 00:08:11.150796",
|
||||||
"hide_toolbar": 0,
|
"modified_by": "Administrator",
|
||||||
"idx": 0,
|
"module": "Accounts",
|
||||||
"image_view": 0,
|
"name": "Payment Reconciliation Payment",
|
||||||
"in_create": 0,
|
"owner": "Administrator",
|
||||||
"is_submittable": 0,
|
"permissions": [],
|
||||||
"issingle": 0,
|
"quick_entry": 1,
|
||||||
"istable": 1,
|
"sort_field": "modified",
|
||||||
"max_attachments": 0,
|
"sort_order": "DESC"
|
||||||
"menu_index": 0,
|
|
||||||
"modified": "2019-01-07 16:52:07.567027",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Accounts",
|
|
||||||
"name": "Payment Reconciliation Payment",
|
|
||||||
"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": 0,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,10 @@ frappe.ui.form.on("POS Profile", "onload", function(frm) {
|
|||||||
return { filters: { selling: 1 } };
|
return { filters: { selling: 1 } };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frm.set_query("tc_name", function() {
|
||||||
|
return { filters: { selling: 1 } };
|
||||||
|
});
|
||||||
|
|
||||||
erpnext.queries.setup_queries(frm, "Warehouse", function() {
|
erpnext.queries.setup_queries(frm, "Warehouse", function() {
|
||||||
return erpnext.queries.warehouse(frm.doc);
|
return erpnext.queries.warehouse(frm.doc);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -337,7 +337,8 @@ class PurchaseInvoice(BuyingController):
|
|||||||
if not self.is_return:
|
if not self.is_return:
|
||||||
self.update_against_document_in_jv()
|
self.update_against_document_in_jv()
|
||||||
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
||||||
self.update_billing_status_in_pr()
|
|
||||||
|
self.update_billing_status_in_pr()
|
||||||
|
|
||||||
# Updating stock ledger should always be called after updating prevdoc status,
|
# Updating stock ledger should always be called after updating prevdoc status,
|
||||||
# because updating ordered qty in bin depends upon updated ordered qty in PO
|
# because updating ordered qty in bin depends upon updated ordered qty in PO
|
||||||
@@ -416,6 +417,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
"account": self.credit_to,
|
"account": self.credit_to,
|
||||||
"party_type": "Supplier",
|
"party_type": "Supplier",
|
||||||
"party": self.supplier,
|
"party": self.supplier,
|
||||||
|
"due_date": self.due_date,
|
||||||
"against": self.against_expense_account,
|
"against": self.against_expense_account,
|
||||||
"credit": grand_total_in_company_currency,
|
"credit": grand_total_in_company_currency,
|
||||||
"credit_in_account_currency": grand_total_in_company_currency \
|
"credit_in_account_currency": grand_total_in_company_currency \
|
||||||
@@ -484,9 +486,13 @@ class PurchaseInvoice(BuyingController):
|
|||||||
"credit": flt(item.rm_supp_cost)
|
"credit": flt(item.rm_supp_cost)
|
||||||
}, warehouse_account[self.supplier_warehouse]["account_currency"], item=item))
|
}, warehouse_account[self.supplier_warehouse]["account_currency"], item=item))
|
||||||
elif not item.is_fixed_asset or (item.is_fixed_asset and is_cwip_accounting_disabled()):
|
elif not item.is_fixed_asset or (item.is_fixed_asset and is_cwip_accounting_disabled()):
|
||||||
|
|
||||||
|
expense_account = (item.expense_account
|
||||||
|
if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account)
|
||||||
|
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
self.get_gl_dict({
|
self.get_gl_dict({
|
||||||
"account": item.expense_account if not item.enable_deferred_expense else item.deferred_expense_account,
|
"account": expense_account,
|
||||||
"against": self.supplier,
|
"against": self.supplier,
|
||||||
"debit": flt(item.base_net_amount, item.precision("base_net_amount")),
|
"debit": flt(item.base_net_amount, item.precision("base_net_amount")),
|
||||||
"debit_in_account_currency": (flt(item.base_net_amount,
|
"debit_in_account_currency": (flt(item.base_net_amount,
|
||||||
@@ -769,7 +775,8 @@ class PurchaseInvoice(BuyingController):
|
|||||||
|
|
||||||
if not self.is_return:
|
if not self.is_return:
|
||||||
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
||||||
self.update_billing_status_in_pr()
|
|
||||||
|
self.update_billing_status_in_pr()
|
||||||
|
|
||||||
# Updating stock ledger should always be called after updating prevdoc status,
|
# Updating stock ledger should always be called after updating prevdoc status,
|
||||||
# because updating ordered qty in bin depends upon updated ordered qty in PO
|
# because updating ordered qty in bin depends upon updated ordered qty in PO
|
||||||
|
|||||||
@@ -734,6 +734,7 @@ class SalesInvoice(SellingController):
|
|||||||
"account": self.debit_to,
|
"account": self.debit_to,
|
||||||
"party_type": "Customer",
|
"party_type": "Customer",
|
||||||
"party": self.customer,
|
"party": self.customer,
|
||||||
|
"due_date": self.due_date,
|
||||||
"against": self.against_income_account,
|
"against": self.against_income_account,
|
||||||
"debit": grand_total_in_company_currency,
|
"debit": grand_total_in_company_currency,
|
||||||
"debit_in_account_currency": grand_total_in_company_currency \
|
"debit_in_account_currency": grand_total_in_company_currency \
|
||||||
@@ -783,10 +784,13 @@ class SalesInvoice(SellingController):
|
|||||||
asset.db_set("disposal_date", self.posting_date)
|
asset.db_set("disposal_date", self.posting_date)
|
||||||
asset.set_status("Sold" if self.docstatus==1 else None)
|
asset.set_status("Sold" if self.docstatus==1 else None)
|
||||||
else:
|
else:
|
||||||
account_currency = get_account_currency(item.income_account)
|
income_account = (item.income_account
|
||||||
|
if (not item.enable_deferred_revenue or self.is_return) else item.deferred_revenue_account)
|
||||||
|
|
||||||
|
account_currency = get_account_currency(income_account)
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
self.get_gl_dict({
|
self.get_gl_dict({
|
||||||
"account": item.income_account if not item.enable_deferred_revenue else item.deferred_revenue_account,
|
"account": income_account,
|
||||||
"against": self.customer,
|
"against": self.customer,
|
||||||
"credit": flt(item.base_net_amount, item.precision("base_net_amount")),
|
"credit": flt(item.base_net_amount, item.precision("base_net_amount")),
|
||||||
"credit_in_account_currency": (flt(item.base_net_amount, item.precision("base_net_amount"))
|
"credit_in_account_currency": (flt(item.base_net_amount, item.precision("base_net_amount"))
|
||||||
|
|||||||
@@ -108,3 +108,14 @@ frappe.query_reports["Accounts Payable"] = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
erpnext.dimension_filters.then((dimensions) => {
|
||||||
|
dimensions.forEach((dimension) => {
|
||||||
|
frappe.query_reports["Accounts Payable"].filters.splice(9, 0 ,{
|
||||||
|
"fieldname": dimension["fieldname"],
|
||||||
|
"label": __(dimension["label"]),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": dimension["document_type"]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -92,3 +92,14 @@ frappe.query_reports["Accounts Payable Summary"] = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
erpnext.dimension_filters.then((dimensions) => {
|
||||||
|
dimensions.forEach((dimension) => {
|
||||||
|
frappe.query_reports["Accounts Payable Summary"].filters.splice(9, 0 ,{
|
||||||
|
"fieldname": dimension["fieldname"],
|
||||||
|
"label": __(dimension["label"]),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": dimension["document_type"]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -172,3 +172,14 @@ frappe.query_reports["Accounts Receivable"] = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
erpnext.dimension_filters.then((dimensions) => {
|
||||||
|
dimensions.forEach((dimension) => {
|
||||||
|
frappe.query_reports["Accounts Receivable"].filters.splice(9, 0 ,{
|
||||||
|
"fieldname": dimension["fieldname"],
|
||||||
|
"label": __(dimension["label"]),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": dimension["document_type"]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe, erpnext
|
import frappe, erpnext
|
||||||
from frappe import _, scrub
|
from frappe import _, scrub
|
||||||
from frappe.utils import getdate, nowdate, flt, cint, formatdate, cstr
|
from frappe.utils import getdate, nowdate, flt, cint, formatdate, cstr
|
||||||
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
|
||||||
|
|
||||||
class ReceivablePayableReport(object):
|
class ReceivablePayableReport(object):
|
||||||
def __init__(self, filters=None):
|
def __init__(self, filters=None):
|
||||||
@@ -553,6 +554,14 @@ class ReceivablePayableReport(object):
|
|||||||
conditions.append("account in (%s)" % ','.join(['%s'] *len(accounts)))
|
conditions.append("account in (%s)" % ','.join(['%s'] *len(accounts)))
|
||||||
values += accounts
|
values += accounts
|
||||||
|
|
||||||
|
accounting_dimensions = get_accounting_dimensions()
|
||||||
|
|
||||||
|
if accounting_dimensions:
|
||||||
|
for dimension in accounting_dimensions:
|
||||||
|
if self.filters.get(dimension):
|
||||||
|
conditions.append("{0} = %s".format(dimension))
|
||||||
|
values.append(self.filters.get(dimension))
|
||||||
|
|
||||||
return " and ".join(conditions), values
|
return " and ".join(conditions), values
|
||||||
|
|
||||||
def get_gl_entries_for(self, party, party_type, against_voucher_type, against_voucher):
|
def get_gl_entries_for(self, party, party_type, against_voucher_type, against_voucher):
|
||||||
@@ -599,9 +608,12 @@ class ReceivablePayableReport(object):
|
|||||||
|
|
||||||
rows = []
|
rows = []
|
||||||
for d in data:
|
for d in data:
|
||||||
|
values = d[self.ageing_col_idx_start : self.ageing_col_idx_start+5]
|
||||||
|
precision = cint(frappe.db.get_default("float_precision")) or 2
|
||||||
|
formatted_values = [frappe.utils.rounded(val, precision) for val in values]
|
||||||
rows.append(
|
rows.append(
|
||||||
{
|
{
|
||||||
'values': d[self.ageing_col_idx_start : self.ageing_col_idx_start+5]
|
'values': formatted_values
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -116,3 +116,14 @@ frappe.query_reports["Accounts Receivable Summary"] = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
erpnext.dimension_filters.then((dimensions) => {
|
||||||
|
dimensions.forEach((dimension) => {
|
||||||
|
frappe.query_reports["Accounts Receivable Summary"].filters.splice(9, 0 ,{
|
||||||
|
"fieldname": dimension["fieldname"],
|
||||||
|
"label": __(dimension["label"]),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": dimension["document_type"]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -63,9 +63,7 @@ frappe.query_reports["Budget Variance Report"] = {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
let dimension_filters = erpnext.get_dimension_filters();
|
erpnext.dimension_filters.then((dimensions) => {
|
||||||
|
|
||||||
dimension_filters.then((dimensions) => {
|
|
||||||
dimensions.forEach((dimension) => {
|
dimensions.forEach((dimension) => {
|
||||||
frappe.query_reports["Budget Variance Report"].filters[4].options.push(dimension["document_type"]);
|
frappe.query_reports["Budget Variance Report"].filters[4].options.push(dimension["document_type"]);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -159,9 +159,7 @@ frappe.query_reports["General Ledger"] = {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
let dimension_filters = erpnext.get_dimension_filters();
|
erpnext.dimension_filters.then((dimensions) => {
|
||||||
|
|
||||||
dimension_filters.then((dimensions) => {
|
|
||||||
dimensions.forEach((dimension) => {
|
dimensions.forEach((dimension) => {
|
||||||
frappe.query_reports["General Ledger"].filters.splice(15, 0 ,{
|
frappe.query_reports["General Ledger"].filters.splice(15, 0 ,{
|
||||||
"fieldname": dimension["fieldname"],
|
"fieldname": dimension["fieldname"],
|
||||||
|
|||||||
@@ -93,4 +93,6 @@ def get_chart_data(filters, columns, income, expense, net_profit_loss):
|
|||||||
else:
|
else:
|
||||||
chart["type"] = "line"
|
chart["type"] = "line"
|
||||||
|
|
||||||
|
chart["fieldtype"] = "Currency"
|
||||||
|
|
||||||
return chart
|
return chart
|
||||||
@@ -16,7 +16,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
|||||||
"fieldname": "based_on",
|
"fieldname": "based_on",
|
||||||
"label": __("Based On"),
|
"label": __("Based On"),
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"options": "Cost Center\nProject",
|
"options": ["Cost Center", "Project"],
|
||||||
"default": "Cost Center",
|
"default": "Cost Center",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
@@ -104,5 +104,10 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
|||||||
"parent_field": "parent_account",
|
"parent_field": "parent_account",
|
||||||
"initial_depth": 3
|
"initial_depth": 3
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
|
erpnext.dimension_filters.then((dimensions) => {
|
||||||
|
dimensions.forEach((dimension) => {
|
||||||
|
frappe.query_reports["Profitability Analysis"].filters[1].options.push(dimension["document_type"]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -24,8 +24,17 @@ def get_accounts_data(based_on, company):
|
|||||||
if based_on == 'cost_center':
|
if based_on == 'cost_center':
|
||||||
return frappe.db.sql("""select name, parent_cost_center as parent_account, cost_center_name as account_name, lft, rgt
|
return frappe.db.sql("""select name, parent_cost_center as parent_account, cost_center_name as account_name, lft, rgt
|
||||||
from `tabCost Center` where company=%s order by name""", company, as_dict=True)
|
from `tabCost Center` where company=%s order by name""", company, as_dict=True)
|
||||||
else:
|
elif based_on == 'project':
|
||||||
return frappe.get_all('Project', fields = ["name"], filters = {'company': company}, order_by = 'name')
|
return frappe.get_all('Project', fields = ["name"], filters = {'company': company}, order_by = 'name')
|
||||||
|
else:
|
||||||
|
filters = {}
|
||||||
|
doctype = frappe.unscrub(based_on)
|
||||||
|
has_company = frappe.db.has_column(doctype, 'company')
|
||||||
|
|
||||||
|
if has_company:
|
||||||
|
filters.update({'company': company})
|
||||||
|
|
||||||
|
return frappe.get_all(doctype, fields = ["name"], filters = filters, order_by = 'name')
|
||||||
|
|
||||||
def get_data(accounts, filters, based_on):
|
def get_data(accounts, filters, based_on):
|
||||||
if not accounts:
|
if not accounts:
|
||||||
@@ -42,7 +51,7 @@ def get_data(accounts, filters, based_on):
|
|||||||
accumulate_values_into_parents(accounts, accounts_by_name)
|
accumulate_values_into_parents(accounts, accounts_by_name)
|
||||||
|
|
||||||
data = prepare_data(accounts, filters, total_row, parent_children_map, based_on)
|
data = prepare_data(accounts, filters, total_row, parent_children_map, based_on)
|
||||||
data = filter_out_zero_value_rows(data, parent_children_map,
|
data = filter_out_zero_value_rows(data, parent_children_map,
|
||||||
show_zero_values=filters.get("show_zero_values"))
|
show_zero_values=filters.get("show_zero_values"))
|
||||||
|
|
||||||
return data
|
return data
|
||||||
@@ -112,14 +121,14 @@ def prepare_data(accounts, filters, total_row, parent_children_map, based_on):
|
|||||||
|
|
||||||
for key in value_fields:
|
for key in value_fields:
|
||||||
row[key] = flt(d.get(key, 0.0), 3)
|
row[key] = flt(d.get(key, 0.0), 3)
|
||||||
|
|
||||||
if abs(row[key]) >= 0.005:
|
if abs(row[key]) >= 0.005:
|
||||||
# ignore zero values
|
# ignore zero values
|
||||||
has_value = True
|
has_value = True
|
||||||
|
|
||||||
row["has_value"] = has_value
|
row["has_value"] = has_value
|
||||||
data.append(row)
|
data.append(row)
|
||||||
|
|
||||||
data.extend([{},total_row])
|
data.extend([{},total_row])
|
||||||
|
|
||||||
return data
|
return data
|
||||||
@@ -174,7 +183,7 @@ def set_gl_entries_by_account(company, from_date, to_date, based_on, gl_entries_
|
|||||||
if from_date:
|
if from_date:
|
||||||
additional_conditions.append("and posting_date >= %(from_date)s")
|
additional_conditions.append("and posting_date >= %(from_date)s")
|
||||||
|
|
||||||
gl_entries = frappe.db.sql("""select posting_date, {based_on} as based_on, debit, credit,
|
gl_entries = frappe.db.sql("""select posting_date, {based_on} as based_on, debit, credit,
|
||||||
is_opening, (select root_type from `tabAccount` where name = account) as type
|
is_opening, (select root_type from `tabAccount` where name = account) as type
|
||||||
from `tabGL Entry` where company=%(company)s
|
from `tabGL Entry` where company=%(company)s
|
||||||
{additional_conditions}
|
{additional_conditions}
|
||||||
|
|||||||
@@ -67,3 +67,14 @@ frappe.query_reports["Sales Register"] = {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
erpnext.dimension_filters.then((dimensions) => {
|
||||||
|
dimensions.forEach((dimension) => {
|
||||||
|
frappe.query_reports["Sales Register"].filters.splice(7, 0 ,{
|
||||||
|
"fieldname": dimension["fieldname"],
|
||||||
|
"label": __(dimension["label"]),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": dimension["document_type"]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import flt
|
from frappe.utils import flt
|
||||||
from frappe import msgprint, _
|
from frappe import msgprint, _
|
||||||
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
return _execute(filters)
|
return _execute(filters)
|
||||||
@@ -163,6 +164,16 @@ def get_conditions(filters):
|
|||||||
where parent=`tabSales Invoice`.name
|
where parent=`tabSales Invoice`.name
|
||||||
and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s)"""
|
and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s)"""
|
||||||
|
|
||||||
|
accounting_dimensions = get_accounting_dimensions()
|
||||||
|
|
||||||
|
if accounting_dimensions:
|
||||||
|
for dimension in accounting_dimensions:
|
||||||
|
if filters.get(dimension):
|
||||||
|
conditions += """ and exists(select name from `tabSales Invoice Item`
|
||||||
|
where parent=`tabSales Invoice`.name
|
||||||
|
and ifnull(`tabSales Invoice Item`.{0}, '') = %({0})s)""".format(dimension)
|
||||||
|
|
||||||
|
|
||||||
return conditions
|
return conditions
|
||||||
|
|
||||||
def get_invoices(filters, additional_query_columns):
|
def get_invoices(filters, additional_query_columns):
|
||||||
|
|||||||
@@ -96,3 +96,14 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
erpnext.dimension_filters.then((dimensions) => {
|
||||||
|
dimensions.forEach((dimension) => {
|
||||||
|
frappe.query_reports["Trial Balance"].filters.splice(5, 0 ,{
|
||||||
|
"fieldname": dimension["fieldname"],
|
||||||
|
"label": __(dimension["label"]),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": dimension["document_type"]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ from frappe import _
|
|||||||
from frappe.utils import flt, getdate, formatdate, cstr
|
from frappe.utils import flt, getdate, formatdate, cstr
|
||||||
from erpnext.accounts.report.financial_statements \
|
from erpnext.accounts.report.financial_statements \
|
||||||
import filter_accounts, set_gl_entries_by_account, filter_out_zero_value_rows
|
import filter_accounts, set_gl_entries_by_account, filter_out_zero_value_rows
|
||||||
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
|
||||||
|
|
||||||
value_fields = ("opening_debit", "opening_credit", "debit", "credit", "closing_debit", "closing_credit")
|
value_fields = ("opening_debit", "opening_credit", "debit", "credit", "closing_debit", "closing_credit")
|
||||||
|
|
||||||
@@ -109,6 +110,25 @@ def get_rootwise_opening_balances(filters, report_type):
|
|||||||
|
|
||||||
additional_conditions += fb_conditions
|
additional_conditions += fb_conditions
|
||||||
|
|
||||||
|
accounting_dimensions = get_accounting_dimensions()
|
||||||
|
|
||||||
|
query_filters = {
|
||||||
|
"company": filters.company,
|
||||||
|
"from_date": filters.from_date,
|
||||||
|
"report_type": report_type,
|
||||||
|
"year_start_date": filters.year_start_date,
|
||||||
|
"finance_book": filters.finance_book,
|
||||||
|
"company_fb": frappe.db.get_value("Company", filters.company, 'default_finance_book')
|
||||||
|
}
|
||||||
|
|
||||||
|
if accounting_dimensions:
|
||||||
|
for dimension in accounting_dimensions:
|
||||||
|
additional_conditions += """ and {0} in (%({0})s) """.format(dimension)
|
||||||
|
|
||||||
|
query_filters.update({
|
||||||
|
dimension: filters.get(dimension)
|
||||||
|
})
|
||||||
|
|
||||||
gle = frappe.db.sql("""
|
gle = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
account, sum(debit) as opening_debit, sum(credit) as opening_credit
|
account, sum(debit) as opening_debit, sum(credit) as opening_credit
|
||||||
@@ -118,16 +138,7 @@ def get_rootwise_opening_balances(filters, report_type):
|
|||||||
{additional_conditions}
|
{additional_conditions}
|
||||||
and (posting_date < %(from_date)s or ifnull(is_opening, 'No') = 'Yes')
|
and (posting_date < %(from_date)s or ifnull(is_opening, 'No') = 'Yes')
|
||||||
and account in (select name from `tabAccount` where report_type=%(report_type)s)
|
and account in (select name from `tabAccount` where report_type=%(report_type)s)
|
||||||
group by account""".format(additional_conditions=additional_conditions),
|
group by account""".format(additional_conditions=additional_conditions), query_filters , as_dict=True)
|
||||||
{
|
|
||||||
"company": filters.company,
|
|
||||||
"from_date": filters.from_date,
|
|
||||||
"report_type": report_type,
|
|
||||||
"year_start_date": filters.year_start_date,
|
|
||||||
"finance_book": filters.finance_book,
|
|
||||||
"company_fb": frappe.db.get_value("Company", filters.company, 'default_finance_book')
|
|
||||||
},
|
|
||||||
as_dict=True)
|
|
||||||
|
|
||||||
opening = frappe._dict()
|
opening = frappe._dict()
|
||||||
for d in gle:
|
for d in gle:
|
||||||
|
|||||||
@@ -435,7 +435,7 @@ def update_reference_in_journal_entry(d, jv_obj):
|
|||||||
jv_obj.flags.ignore_validate_update_after_submit = True
|
jv_obj.flags.ignore_validate_update_after_submit = True
|
||||||
jv_obj.save(ignore_permissions=True)
|
jv_obj.save(ignore_permissions=True)
|
||||||
|
|
||||||
def update_reference_in_payment_entry(d, payment_entry):
|
def update_reference_in_payment_entry(d, payment_entry, do_not_save=False):
|
||||||
reference_details = {
|
reference_details = {
|
||||||
"reference_doctype": d.against_voucher_type,
|
"reference_doctype": d.against_voucher_type,
|
||||||
"reference_name": d.against_voucher,
|
"reference_name": d.against_voucher,
|
||||||
@@ -466,7 +466,17 @@ def update_reference_in_payment_entry(d, payment_entry):
|
|||||||
payment_entry.setup_party_account_field()
|
payment_entry.setup_party_account_field()
|
||||||
payment_entry.set_missing_values()
|
payment_entry.set_missing_values()
|
||||||
payment_entry.set_amounts()
|
payment_entry.set_amounts()
|
||||||
payment_entry.save(ignore_permissions=True)
|
|
||||||
|
if d.difference_amount and d.difference_account:
|
||||||
|
payment_entry.set_gain_or_loss(account_details={
|
||||||
|
'account': d.difference_account,
|
||||||
|
'cost_center': payment_entry.cost_center or frappe.get_cached_value('Company',
|
||||||
|
payment_entry.company, "cost_center"),
|
||||||
|
'amount': d.difference_amount
|
||||||
|
})
|
||||||
|
|
||||||
|
if not do_not_save:
|
||||||
|
payment_entry.save(ignore_permissions=True)
|
||||||
|
|
||||||
def unlink_ref_doc_from_payment_entries(ref_doc):
|
def unlink_ref_doc_from_payment_entries(ref_doc):
|
||||||
remove_ref_doc_link_from_jv(ref_doc.doctype, ref_doc.name)
|
remove_ref_doc_link_from_jv(ref_doc.doctype, ref_doc.name)
|
||||||
@@ -618,7 +628,7 @@ def get_held_invoices(party_type, party):
|
|||||||
return held_invoices
|
return held_invoices
|
||||||
|
|
||||||
|
|
||||||
def get_outstanding_invoices(party_type, party, account, condition=None):
|
def get_outstanding_invoices(party_type, party, account, condition=None, filters=None):
|
||||||
outstanding_invoices = []
|
outstanding_invoices = []
|
||||||
precision = frappe.get_precision("Sales Invoice", "outstanding_amount") or 2
|
precision = frappe.get_precision("Sales Invoice", "outstanding_amount") or 2
|
||||||
|
|
||||||
@@ -634,7 +644,8 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
|
|||||||
|
|
||||||
invoice_list = frappe.db.sql("""
|
invoice_list = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
voucher_no, voucher_type, posting_date, ifnull(sum({dr_or_cr}), 0) as invoice_amount
|
voucher_no, voucher_type, posting_date, due_date,
|
||||||
|
ifnull(sum({dr_or_cr}), 0) as invoice_amount
|
||||||
from
|
from
|
||||||
`tabGL Entry`
|
`tabGL Entry`
|
||||||
where
|
where
|
||||||
@@ -667,7 +678,7 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
|
|||||||
""".format(payment_dr_or_cr=payment_dr_or_cr), {
|
""".format(payment_dr_or_cr=payment_dr_or_cr), {
|
||||||
"party_type": party_type,
|
"party_type": party_type,
|
||||||
"party": party,
|
"party": party,
|
||||||
"account": account,
|
"account": account
|
||||||
}, as_dict=True)
|
}, as_dict=True)
|
||||||
|
|
||||||
pe_map = frappe._dict()
|
pe_map = frappe._dict()
|
||||||
@@ -678,10 +689,12 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
|
|||||||
payment_amount = pe_map.get((d.voucher_type, d.voucher_no), 0)
|
payment_amount = pe_map.get((d.voucher_type, d.voucher_no), 0)
|
||||||
outstanding_amount = flt(d.invoice_amount - payment_amount, precision)
|
outstanding_amount = flt(d.invoice_amount - payment_amount, precision)
|
||||||
if outstanding_amount > 0.5 / (10**precision):
|
if outstanding_amount > 0.5 / (10**precision):
|
||||||
if not d.voucher_type == "Purchase Invoice" or d.voucher_no not in held_invoices:
|
if (filters.get("outstanding_amt_greater_than") and
|
||||||
due_date = frappe.db.get_value(
|
not (outstanding_amount >= filters.get("outstanding_amt_greater_than") and
|
||||||
d.voucher_type, d.voucher_no, "posting_date" if party_type == "Employee" else "due_date")
|
outstanding_amount <= filters.get("outstanding_amt_less_than"))):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not d.voucher_type == "Purchase Invoice" or d.voucher_no not in held_invoices:
|
||||||
outstanding_invoices.append(
|
outstanding_invoices.append(
|
||||||
frappe._dict({
|
frappe._dict({
|
||||||
'voucher_no': d.voucher_no,
|
'voucher_no': d.voucher_no,
|
||||||
@@ -690,7 +703,7 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
|
|||||||
'invoice_amount': flt(d.invoice_amount),
|
'invoice_amount': flt(d.invoice_amount),
|
||||||
'payment_amount': payment_amount,
|
'payment_amount': payment_amount,
|
||||||
'outstanding_amount': outstanding_amount,
|
'outstanding_amount': outstanding_amount,
|
||||||
'due_date': due_date
|
'due_date': d.due_date
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -3,43 +3,6 @@ from frappe import _
|
|||||||
|
|
||||||
def get_data():
|
def get_data():
|
||||||
return [
|
return [
|
||||||
{
|
|
||||||
"label": _("Production"),
|
|
||||||
"icon": "fa fa-star",
|
|
||||||
"items": [
|
|
||||||
{
|
|
||||||
"type": "doctype",
|
|
||||||
"name": "Work Order",
|
|
||||||
"description": _("Orders released for production."),
|
|
||||||
"onboard": 1,
|
|
||||||
"dependencies": ["Item", "BOM"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "doctype",
|
|
||||||
"name": "Production Plan",
|
|
||||||
"description": _("Generate Material Requests (MRP) and Work Orders."),
|
|
||||||
"onboard": 1,
|
|
||||||
"dependencies": ["Item", "BOM"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "doctype",
|
|
||||||
"name": "Stock Entry",
|
|
||||||
"onboard": 1,
|
|
||||||
"dependencies": ["Item"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "doctype",
|
|
||||||
"name": "Timesheet",
|
|
||||||
"description": _("Time Sheet for manufacturing."),
|
|
||||||
"onboard": 1,
|
|
||||||
"dependencies": ["Activity Type"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "doctype",
|
|
||||||
"name": "Job Card"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"label": _("Bill of Materials"),
|
"label": _("Bill of Materials"),
|
||||||
"items": [
|
"items": [
|
||||||
@@ -85,6 +48,43 @@ def get_data():
|
|||||||
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": _("Production"),
|
||||||
|
"icon": "fa fa-star",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Work Order",
|
||||||
|
"description": _("Orders released for production."),
|
||||||
|
"onboard": 1,
|
||||||
|
"dependencies": ["Item", "BOM"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Production Plan",
|
||||||
|
"description": _("Generate Material Requests (MRP) and Work Orders."),
|
||||||
|
"onboard": 1,
|
||||||
|
"dependencies": ["Item", "BOM"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Stock Entry",
|
||||||
|
"onboard": 1,
|
||||||
|
"dependencies": ["Item"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Timesheet",
|
||||||
|
"description": _("Time Sheet for manufacturing."),
|
||||||
|
"onboard": 1,
|
||||||
|
"dependencies": ["Activity Type"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Job Card"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": _("Tools"),
|
"label": _("Tools"),
|
||||||
"icon": "fa fa-wrench",
|
"icon": "fa fa-wrench",
|
||||||
|
|||||||
@@ -97,4 +97,15 @@ def get_data():
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": _("Settings"),
|
||||||
|
"icon": "fa fa-list",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Support Settings",
|
||||||
|
"label": _("Support Settings"),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
]
|
]
|
||||||
@@ -98,8 +98,8 @@ def validate_item_attribute_value(attributes_list, attribute, attribute_value, i
|
|||||||
if allow_rename_attribute_value:
|
if allow_rename_attribute_value:
|
||||||
pass
|
pass
|
||||||
elif attribute_value not in attributes_list:
|
elif attribute_value not in attributes_list:
|
||||||
frappe.throw(_("Value {0} for Attribute {1} does not exist in the list of valid Item Attribute Values for Item {2}").format(
|
frappe.throw(_("The value {0} is already assigned to an exisiting Item {2}.").format(
|
||||||
attribute_value, attribute, item), InvalidItemAttributeValueError, title=_('Invalid Attribute'))
|
attribute_value, attribute, item), InvalidItemAttributeValueError, title=_('Rename Not Allowed'))
|
||||||
|
|
||||||
def get_attribute_values(item):
|
def get_attribute_values(item):
|
||||||
if not frappe.flags.attribute_values:
|
if not frappe.flags.attribute_values:
|
||||||
|
|||||||
@@ -206,10 +206,11 @@ def bom(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
|
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
|
||||||
idx desc, name
|
idx desc, name
|
||||||
limit %(start)s, %(page_len)s """.format(
|
limit %(start)s, %(page_len)s """.format(
|
||||||
fcond=get_filters_cond(doctype, filters, conditions),
|
fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'),
|
||||||
mcond=get_match_cond(doctype),
|
mcond=get_match_cond(doctype),
|
||||||
key=searchfield), {
|
key=frappe.db.escape(searchfield)),
|
||||||
'txt': '%' + txt + '%',
|
{
|
||||||
|
'txt': "%"+frappe.db.escape(txt)+"%",
|
||||||
'_txt': txt.replace("%", ""),
|
'_txt': txt.replace("%", ""),
|
||||||
'start': start or 0,
|
'start': start or 0,
|
||||||
'page_len': page_len or 20
|
'page_len': page_len or 20
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ def validate_returned_items(doc):
|
|||||||
|
|
||||||
items_returned = False
|
items_returned = False
|
||||||
for d in doc.get("items"):
|
for d in doc.get("items"):
|
||||||
if flt(d.qty) < 0 or d.get('received_qty') < 0:
|
if d.item_code and (flt(d.qty) < 0 or d.get('received_qty') < 0):
|
||||||
if d.item_code not in valid_items:
|
if d.item_code not in valid_items:
|
||||||
frappe.throw(_("Row # {0}: Returned Item {1} does not exists in {2} {3}")
|
frappe.throw(_("Row # {0}: Returned Item {1} does not exists in {2} {3}")
|
||||||
.format(d.idx, d.item_code, doc.doctype, doc.return_against))
|
.format(d.idx, d.item_code, doc.doctype, doc.return_against))
|
||||||
@@ -107,6 +107,9 @@ def validate_returned_items(doc):
|
|||||||
|
|
||||||
items_returned = True
|
items_returned = True
|
||||||
|
|
||||||
|
elif d.item_name:
|
||||||
|
items_returned = True
|
||||||
|
|
||||||
if not items_returned:
|
if not items_returned:
|
||||||
frappe.throw(_("Atleast one item should be entered with negative quantity in return document"))
|
frappe.throw(_("Atleast one item should be entered with negative quantity in return document"))
|
||||||
|
|
||||||
|
|||||||
@@ -294,7 +294,7 @@ class StatusUpdater(Document):
|
|||||||
frappe.db.sql("""update `tab%(target_parent_dt)s`
|
frappe.db.sql("""update `tab%(target_parent_dt)s`
|
||||||
set %(target_parent_field)s = round(
|
set %(target_parent_field)s = round(
|
||||||
ifnull((select
|
ifnull((select
|
||||||
ifnull(sum(if(%(target_ref_field)s > %(target_field)s, abs(%(target_field)s), abs(%(target_ref_field)s))), 0)
|
ifnull(sum(if(abs(%(target_ref_field)s) > abs(%(target_field)s), abs(%(target_field)s), abs(%(target_ref_field)s))), 0)
|
||||||
/ sum(abs(%(target_ref_field)s)) * 100
|
/ sum(abs(%(target_ref_field)s)) * 100
|
||||||
from `tab%(target_dt)s` where parent="%(name)s" having sum(abs(%(target_ref_field)s)) > 0), 0), 6)
|
from `tab%(target_dt)s` where parent="%(name)s" having sum(abs(%(target_ref_field)s)) > 0), 0), 6)
|
||||||
%(update_modified)s
|
%(update_modified)s
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from erpnext.hr.doctype.employee_onboarding.employee_onboarding import Incomplet
|
|||||||
class TestEmployeeOnboarding(unittest.TestCase):
|
class TestEmployeeOnboarding(unittest.TestCase):
|
||||||
def test_employee_onboarding_incomplete_task(self):
|
def test_employee_onboarding_incomplete_task(self):
|
||||||
if frappe.db.exists('Employee Onboarding', {'employee_name': 'Test Researcher'}):
|
if frappe.db.exists('Employee Onboarding', {'employee_name': 'Test Researcher'}):
|
||||||
return frappe.get_doc('Employee Onboarding', {'employee_name': 'Test Researcher'})
|
frappe.delete_doc('Employee Onboarding', {'employee_name': 'Test Researcher'})
|
||||||
_set_up()
|
_set_up()
|
||||||
applicant = get_job_applicant()
|
applicant = get_job_applicant()
|
||||||
onboarding = frappe.new_doc('Employee Onboarding')
|
onboarding = frappe.new_doc('Employee Onboarding')
|
||||||
@@ -39,9 +39,10 @@ class TestEmployeeOnboarding(unittest.TestCase):
|
|||||||
|
|
||||||
# complete the task
|
# complete the task
|
||||||
project = frappe.get_doc('Project', onboarding.project)
|
project = frappe.get_doc('Project', onboarding.project)
|
||||||
project.load_tasks()
|
for task in frappe.get_all('Task', dict(project=project.name)):
|
||||||
project.tasks[0].status = 'Completed'
|
task = frappe.get_doc('Task', task.name)
|
||||||
project.save()
|
task.status = 'Completed'
|
||||||
|
task.save()
|
||||||
|
|
||||||
# make employee
|
# make employee
|
||||||
onboarding.reload()
|
onboarding.reload()
|
||||||
@@ -71,4 +72,3 @@ def _set_up():
|
|||||||
project = "Employee Onboarding : Test Researcher - test@researcher.com"
|
project = "Employee Onboarding : Test Researcher - test@researcher.com"
|
||||||
frappe.db.sql("delete from tabProject where name=%s", project)
|
frappe.db.sql("delete from tabProject where name=%s", project)
|
||||||
frappe.db.sql("delete from tabTask where project=%s", project)
|
frappe.db.sql("delete from tabTask where project=%s", project)
|
||||||
frappe.db.sql("delete from `tabProject Task` where parent=%s", project)
|
|
||||||
|
|||||||
@@ -10,33 +10,36 @@ from erpnext.accounts.doctype.account.test_account import create_account
|
|||||||
|
|
||||||
test_records = frappe.get_test_records('Expense Claim')
|
test_records = frappe.get_test_records('Expense Claim')
|
||||||
test_dependencies = ['Employee']
|
test_dependencies = ['Employee']
|
||||||
|
company_name = '_Test Company 4'
|
||||||
|
|
||||||
|
|
||||||
class TestExpenseClaim(unittest.TestCase):
|
class TestExpenseClaim(unittest.TestCase):
|
||||||
def test_total_expense_claim_for_project(self):
|
def test_total_expense_claim_for_project(self):
|
||||||
frappe.db.sql("""delete from `tabTask` where project = "_Test Project 1" """)
|
frappe.db.sql("""delete from `tabTask` where project = "_Test Project 1" """)
|
||||||
frappe.db.sql("""delete from `tabProject Task` where parent = "_Test Project 1" """)
|
|
||||||
frappe.db.sql("""delete from `tabProject` where name = "_Test Project 1" """)
|
frappe.db.sql("""delete from `tabProject` where name = "_Test Project 1" """)
|
||||||
frappe.db.sql("delete from `tabExpense Claim` where project='_Test Project 1'")
|
frappe.db.sql("update `tabExpense Claim` set project = '', task = ''")
|
||||||
|
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
"project_name": "_Test Project 1",
|
"project_name": "_Test Project 1",
|
||||||
"doctype": "Project",
|
"doctype": "Project"
|
||||||
}).save()
|
}).save()
|
||||||
|
|
||||||
task = frappe.get_doc({
|
task = frappe.get_doc(dict(
|
||||||
"doctype": "Task",
|
doctype = 'Task',
|
||||||
"subject": "_Test Project Task 1",
|
subject = '_Test Project Task 1',
|
||||||
"project": "_Test Project 1"
|
status = 'Open',
|
||||||
}).save()
|
project = '_Test Project 1'
|
||||||
|
)).insert()
|
||||||
|
|
||||||
task_name = frappe.db.get_value("Task", {"project": "_Test Project 1"})
|
task_name = task.name
|
||||||
payable_account = get_payable_account("Wind Power LLC")
|
payable_account = get_payable_account(company_name)
|
||||||
make_expense_claim(payable_account, 300, 200, "Wind Power LLC","Travel Expenses - WP", "_Test Project 1", task_name)
|
|
||||||
|
make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", "_Test Project 1", task_name)
|
||||||
|
|
||||||
self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200)
|
self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200)
|
||||||
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200)
|
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200)
|
||||||
|
|
||||||
expense_claim2 = make_expense_claim(payable_account, 600, 500, "Wind Power LLC", "Travel Expenses - WP","_Test Project 1", task_name)
|
expense_claim2 = make_expense_claim(payable_account, 600, 500, company_name, "Travel Expenses - _TC4","_Test Project 1", task_name)
|
||||||
|
|
||||||
self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 700)
|
self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 700)
|
||||||
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 700)
|
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 700)
|
||||||
@@ -48,8 +51,8 @@ class TestExpenseClaim(unittest.TestCase):
|
|||||||
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200)
|
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200)
|
||||||
|
|
||||||
def test_expense_claim_status(self):
|
def test_expense_claim_status(self):
|
||||||
payable_account = get_payable_account("Wind Power LLC")
|
payable_account = get_payable_account(company_name)
|
||||||
expense_claim = make_expense_claim(payable_account, 300, 200, "Wind Power LLC", "Travel Expenses - WP")
|
expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4")
|
||||||
|
|
||||||
je_dict = make_bank_entry("Expense Claim", expense_claim.name)
|
je_dict = make_bank_entry("Expense Claim", expense_claim.name)
|
||||||
je = frappe.get_doc(je_dict)
|
je = frappe.get_doc(je_dict)
|
||||||
@@ -66,9 +69,9 @@ class TestExpenseClaim(unittest.TestCase):
|
|||||||
self.assertEqual(expense_claim.status, "Unpaid")
|
self.assertEqual(expense_claim.status, "Unpaid")
|
||||||
|
|
||||||
def test_expense_claim_gl_entry(self):
|
def test_expense_claim_gl_entry(self):
|
||||||
payable_account = get_payable_account("Wind Power LLC")
|
payable_account = get_payable_account(company_name)
|
||||||
taxes = generate_taxes()
|
taxes = generate_taxes()
|
||||||
expense_claim = make_expense_claim(payable_account, 300, 200, "Wind Power LLC", "Travel Expenses - WP", do_not_submit=True, taxes=taxes)
|
expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", do_not_submit=True, taxes=taxes)
|
||||||
expense_claim.submit()
|
expense_claim.submit()
|
||||||
|
|
||||||
gl_entries = frappe.db.sql("""select account, debit, credit
|
gl_entries = frappe.db.sql("""select account, debit, credit
|
||||||
@@ -78,9 +81,9 @@ class TestExpenseClaim(unittest.TestCase):
|
|||||||
self.assertTrue(gl_entries)
|
self.assertTrue(gl_entries)
|
||||||
|
|
||||||
expected_values = dict((d[0], d) for d in [
|
expected_values = dict((d[0], d) for d in [
|
||||||
['CGST - WP',10.0, 0.0],
|
['CGST - _TC4',18.0, 0.0],
|
||||||
[payable_account, 0.0, 210.0],
|
[payable_account, 0.0, 218.0],
|
||||||
["Travel Expenses - WP", 200.0, 0.0]
|
["Travel Expenses - _TC4", 200.0, 0.0]
|
||||||
])
|
])
|
||||||
|
|
||||||
for gle in gl_entries:
|
for gle in gl_entries:
|
||||||
@@ -89,14 +92,14 @@ class TestExpenseClaim(unittest.TestCase):
|
|||||||
self.assertEquals(expected_values[gle.account][2], gle.credit)
|
self.assertEquals(expected_values[gle.account][2], gle.credit)
|
||||||
|
|
||||||
def test_rejected_expense_claim(self):
|
def test_rejected_expense_claim(self):
|
||||||
payable_account = get_payable_account("Wind Power LLC")
|
payable_account = get_payable_account(company_name)
|
||||||
expense_claim = frappe.get_doc({
|
expense_claim = frappe.get_doc({
|
||||||
"doctype": "Expense Claim",
|
"doctype": "Expense Claim",
|
||||||
"employee": "_T-Employee-00001",
|
"employee": "_T-Employee-00001",
|
||||||
"payable_account": payable_account,
|
"payable_account": payable_account,
|
||||||
"approval_status": "Rejected",
|
"approval_status": "Rejected",
|
||||||
"expenses":
|
"expenses":
|
||||||
[{ "expense_type": "Travel", "default_account": "Travel Expenses - WP", "amount": 300, "sanctioned_amount": 200 }]
|
[{ "expense_type": "Travel", "default_account": "Travel Expenses - _TC4", "amount": 300, "sanctioned_amount": 200 }]
|
||||||
})
|
})
|
||||||
expense_claim.submit()
|
expense_claim.submit()
|
||||||
|
|
||||||
@@ -111,9 +114,9 @@ def get_payable_account(company):
|
|||||||
|
|
||||||
def generate_taxes():
|
def generate_taxes():
|
||||||
parent_account = frappe.db.get_value('Account',
|
parent_account = frappe.db.get_value('Account',
|
||||||
{'company': "Wind Power LLC", 'is_group':1, 'account_type': 'Tax'},
|
{'company': company_name, 'is_group':1, 'account_type': 'Tax'},
|
||||||
'name')
|
'name')
|
||||||
account = create_account(company="Wind Power LLC", account_name="CGST", account_type="Tax", parent_account=parent_account)
|
account = create_account(company=company_name, account_name="CGST", account_type="Tax", parent_account=parent_account)
|
||||||
return {'taxes':[{
|
return {'taxes':[{
|
||||||
"account_head": account,
|
"account_head": account,
|
||||||
"rate": 0,
|
"rate": 0,
|
||||||
@@ -124,15 +127,18 @@ def generate_taxes():
|
|||||||
|
|
||||||
def make_expense_claim(payable_account, amount, sanctioned_amount, company, account, project=None, task_name=None, do_not_submit=False, taxes=None):
|
def make_expense_claim(payable_account, amount, sanctioned_amount, company, account, project=None, task_name=None, do_not_submit=False, taxes=None):
|
||||||
employee = frappe.db.get_value("Employee", {"status": "Active"})
|
employee = frappe.db.get_value("Employee", {"status": "Active"})
|
||||||
|
currency = frappe.db.get_value('Company', company, 'default_currency')
|
||||||
expense_claim = {
|
expense_claim = {
|
||||||
"doctype": "Expense Claim",
|
"doctype": "Expense Claim",
|
||||||
"employee": employee,
|
"employee": employee,
|
||||||
"payable_account": payable_account,
|
"payable_account": payable_account,
|
||||||
"approval_status": "Approved",
|
"approval_status": "Approved",
|
||||||
"company": company,
|
"company": company,
|
||||||
|
'currency': currency,
|
||||||
"expenses":
|
"expenses":
|
||||||
[{"expense_type": "Travel",
|
[{"expense_type": "Travel",
|
||||||
"default_account": account,
|
"default_account": account,
|
||||||
|
'currency': currency,
|
||||||
"amount": amount,
|
"amount": amount,
|
||||||
"sanctioned_amount": sanctioned_amount}]}
|
"sanctioned_amount": sanctioned_amount}]}
|
||||||
if taxes:
|
if taxes:
|
||||||
|
|||||||
@@ -4,6 +4,12 @@
|
|||||||
frappe.provide("erpnext.job_offer");
|
frappe.provide("erpnext.job_offer");
|
||||||
|
|
||||||
frappe.ui.form.on("Job Offer", {
|
frappe.ui.form.on("Job Offer", {
|
||||||
|
onload: function (frm) {
|
||||||
|
frm.set_query("select_terms", function() {
|
||||||
|
return { filters: { hr: 1 } };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
select_terms: function (frm) {
|
select_terms: function (frm) {
|
||||||
erpnext.utils.get_terms(frm.doc.select_terms, frm.doc, function (r) {
|
erpnext.utils.get_terms(frm.doc.select_terms, frm.doc, function (r) {
|
||||||
if (!r.exc) {
|
if (!r.exc) {
|
||||||
|
|||||||
@@ -618,7 +618,7 @@ class SalarySlip(TransactionBase):
|
|||||||
elif not self.payment_days and not self.salary_slip_based_on_timesheet and cint(row.depends_on_payment_days):
|
elif not self.payment_days and not self.salary_slip_based_on_timesheet and cint(row.depends_on_payment_days):
|
||||||
amount, additional_amount = 0, 0
|
amount, additional_amount = 0, 0
|
||||||
elif not row.amount:
|
elif not row.amount:
|
||||||
amount = row.default_amount + row.additional_amount
|
amount = flt(row.default_amount) + flt(row.additional_amount)
|
||||||
|
|
||||||
# apply rounding
|
# apply rounding
|
||||||
if frappe.get_cached_value("Salary Component", row.salary_component, "round_to_the_nearest_integer"):
|
if frappe.get_cached_value("Salary Component", row.salary_component, "round_to_the_nearest_integer"):
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Blanket Order', {
|
frappe.ui.form.on('Blanket Order', {
|
||||||
|
onload: function(frm) {
|
||||||
|
frm.trigger('set_tc_name_filter');
|
||||||
|
},
|
||||||
|
|
||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
frm.add_fetch("customer", "customer_name", "customer_name");
|
frm.add_fetch("customer", "customer_name", "customer_name");
|
||||||
frm.add_fetch("supplier", "supplier_name", "supplier_name");
|
frm.add_fetch("supplier", "supplier_name", "supplier_name");
|
||||||
@@ -44,4 +48,23 @@ frappe.ui.form.on('Blanket Order', {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
set_tc_name_filter: function(frm) {
|
||||||
|
if (frm.doc.blanket_order_type === 'Selling') {
|
||||||
|
frm.set_query("tc_name", function() {
|
||||||
|
return { filters: { selling: 1 } };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (frm.doc.blanket_order_type === 'Purchasing') {
|
||||||
|
frm.set_query("tc_name", function() {
|
||||||
|
return { filters: { buying: 1 } };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
blanket_order_type: function (frm) {
|
||||||
|
frm.trigger('set_tc_name_filter');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
from frappe import _
|
||||||
|
|
||||||
|
def get_data():
|
||||||
|
return {
|
||||||
|
'fieldname': 'blanket_order',
|
||||||
|
'transactions': [
|
||||||
|
{
|
||||||
|
'items': ['Purchase Order', 'Sales Order']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
27
erpnext/manufacturing/doctype/bom/bom_dashboard.py
Normal file
27
erpnext/manufacturing/doctype/bom/bom_dashboard.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
from frappe import _
|
||||||
|
|
||||||
|
def get_data():
|
||||||
|
return {
|
||||||
|
'fieldname': 'bom_no',
|
||||||
|
'non_standard_fieldnames': {
|
||||||
|
'Item': 'default_bom',
|
||||||
|
'Purchase Order': 'bom',
|
||||||
|
'Purchase Receipt': 'bom',
|
||||||
|
'Purchase Invoice': 'bom'
|
||||||
|
},
|
||||||
|
'transactions': [
|
||||||
|
{
|
||||||
|
'label': _('Stock'),
|
||||||
|
'items': ['Item', 'Stock Entry', 'Quality Inspection']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _('Manufacture'),
|
||||||
|
'items': ['BOM', 'Work Order', 'Job Card', 'Production Plan']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _('Purchase'),
|
||||||
|
'items': ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -10,4 +10,4 @@ def get_data():
|
|||||||
'items': ['Material Request', 'Stock Entry']
|
'items': ['Material Request', 'Stock Entry']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
from frappe import _
|
||||||
|
|
||||||
|
def get_data():
|
||||||
|
return {
|
||||||
|
'fieldname': 'operation',
|
||||||
|
'transactions': [
|
||||||
|
{
|
||||||
|
'label': _('Manufacture'),
|
||||||
|
'items': ['BOM', 'Work Order', 'Job Card', 'Timesheet']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
12
erpnext/manufacturing/doctype/routing/routing_dashboard.py
Normal file
12
erpnext/manufacturing/doctype/routing/routing_dashboard.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
from frappe import _
|
||||||
|
|
||||||
|
def get_data():
|
||||||
|
return {
|
||||||
|
'fieldname': 'routing',
|
||||||
|
'transactions': [
|
||||||
|
{
|
||||||
|
'items': ['BOM']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
from frappe import _
|
||||||
|
|
||||||
|
def get_data():
|
||||||
|
return {
|
||||||
|
'fieldname': 'workstation',
|
||||||
|
'transactions': [
|
||||||
|
{
|
||||||
|
'label': _('Manufacture'),
|
||||||
|
'items': ['BOM', 'Routing', 'Work Order', 'Job Card', 'Operation', 'Timesheet']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -533,7 +533,7 @@ erpnext.patches.v11_0.create_department_records_for_each_company
|
|||||||
erpnext.patches.v11_0.make_location_from_warehouse
|
erpnext.patches.v11_0.make_location_from_warehouse
|
||||||
erpnext.patches.v11_0.make_asset_finance_book_against_old_entries
|
erpnext.patches.v11_0.make_asset_finance_book_against_old_entries
|
||||||
erpnext.patches.v11_0.check_buying_selling_in_currency_exchange
|
erpnext.patches.v11_0.check_buying_selling_in_currency_exchange
|
||||||
erpnext.patches.v11_0.move_item_defaults_to_child_table_for_multicompany #02-07-2018
|
erpnext.patches.v11_0.move_item_defaults_to_child_table_for_multicompany #02-07-2018 #19-06-2019
|
||||||
erpnext.patches.v11_0.refactor_erpnext_shopify #2018-09-07
|
erpnext.patches.v11_0.refactor_erpnext_shopify #2018-09-07
|
||||||
erpnext.patches.v11_0.rename_overproduction_percent_field
|
erpnext.patches.v11_0.rename_overproduction_percent_field
|
||||||
erpnext.patches.v11_0.update_backflush_subcontract_rm_based_on_bom
|
erpnext.patches.v11_0.update_backflush_subcontract_rm_based_on_bom
|
||||||
@@ -615,3 +615,7 @@ erpnext.patches.v11_1.set_missing_opportunity_from
|
|||||||
erpnext.patches.v12_0.set_quotation_status
|
erpnext.patches.v12_0.set_quotation_status
|
||||||
erpnext.patches.v12_0.set_priority_for_support
|
erpnext.patches.v12_0.set_priority_for_support
|
||||||
erpnext.patches.v12_0.delete_priority_property_setter
|
erpnext.patches.v12_0.delete_priority_property_setter
|
||||||
|
execute:frappe.delete_doc("DocType", "Project Task")
|
||||||
|
erpnext.patches.v11_1.update_default_supplier_in_item_defaults
|
||||||
|
erpnext.patches.v12_0.update_due_date_in_gle
|
||||||
|
erpnext.patches.v12_0.add_default_buying_selling_terms_in_company
|
||||||
|
|||||||
@@ -17,10 +17,8 @@ def execute():
|
|||||||
frappe.reload_doc('stock', 'doctype', 'item_default')
|
frappe.reload_doc('stock', 'doctype', 'item_default')
|
||||||
frappe.reload_doc('stock', 'doctype', 'item')
|
frappe.reload_doc('stock', 'doctype', 'item')
|
||||||
|
|
||||||
if frappe.db.a_row_exists('Item Default'): return
|
|
||||||
|
|
||||||
companies = frappe.get_all("Company")
|
companies = frappe.get_all("Company")
|
||||||
if len(companies) == 1:
|
if len(companies) == 1 and not frappe.get_all("Item Default", limit=1):
|
||||||
try:
|
try:
|
||||||
frappe.db.sql('''
|
frappe.db.sql('''
|
||||||
INSERT INTO `tabItem Default`
|
INSERT INTO `tabItem Default`
|
||||||
@@ -35,32 +33,64 @@ def execute():
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
item_details = frappe.get_all("Item", fields=["name", "default_warehouse", "buying_cost_center",
|
item_details = frappe.db.sql(""" SELECT name, default_warehouse,
|
||||||
"expense_account", "selling_cost_center", "income_account"], limit=100)
|
buying_cost_center, expense_account, selling_cost_center, income_account
|
||||||
|
FROM tabItem
|
||||||
|
WHERE
|
||||||
|
name not in (select distinct parent from `tabItem Default`) and ifnull(disabled, 0) = 0"""
|
||||||
|
, as_dict=1)
|
||||||
|
|
||||||
for item in item_details:
|
items_default_data = {}
|
||||||
item_defaults = []
|
for item_data in item_details:
|
||||||
|
for d in [["default_warehouse", "Warehouse"], ["expense_account", "Account"],
|
||||||
|
["income_account", "Account"], ["buying_cost_center", "Cost Center"],
|
||||||
|
["selling_cost_center", "Cost Center"]]:
|
||||||
|
if item_data.get(d[0]):
|
||||||
|
company = frappe.get_value(d[1], item_data.get(d[0]), "company", cache=True)
|
||||||
|
|
||||||
def insert_into_item_defaults(doc_field_name, doc_field_value, company):
|
if item_data.name not in items_default_data:
|
||||||
for d in item_defaults:
|
items_default_data[item_data.name] = {}
|
||||||
if d.get("company") == company:
|
|
||||||
d[doc_field_name] = doc_field_value
|
|
||||||
return
|
|
||||||
item_defaults.append({
|
|
||||||
"company": company,
|
|
||||||
doc_field_name: doc_field_value
|
|
||||||
})
|
|
||||||
|
|
||||||
for d in [
|
company_wise_data = items_default_data[item_data.name]
|
||||||
["default_warehouse", "Warehouse"], ["expense_account", "Account"], ["income_account", "Account"],
|
|
||||||
["buying_cost_center", "Cost Center"], ["selling_cost_center", "Cost Center"]
|
|
||||||
]:
|
|
||||||
if item.get(d[0]):
|
|
||||||
company = frappe.get_value(d[1], item.get(d[0]), "company", cache=True)
|
|
||||||
insert_into_item_defaults(d[0], item.get(d[0]), company)
|
|
||||||
|
|
||||||
doc = frappe.get_doc("Item", item.name)
|
if company not in company_wise_data:
|
||||||
doc.extend("item_defaults", item_defaults)
|
company_wise_data[company] = {}
|
||||||
|
|
||||||
for child_doc in doc.item_defaults:
|
default_data = company_wise_data[company]
|
||||||
child_doc.db_insert()
|
default_data[d[0]] = item_data.get(d[0])
|
||||||
|
|
||||||
|
to_insert_data = []
|
||||||
|
|
||||||
|
# items_default_data data structure will be as follow
|
||||||
|
# {
|
||||||
|
# 'item_code 1': {'company 1': {'default_warehouse': 'Test Warehouse 1'}},
|
||||||
|
# 'item_code 2': {
|
||||||
|
# 'company 1': {'default_warehouse': 'Test Warehouse 1'},
|
||||||
|
# 'company 2': {'default_warehouse': 'Test Warehouse 1'}
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
|
||||||
|
for item_code, companywise_item_data in items_default_data.items():
|
||||||
|
for company, item_default_data in companywise_item_data.items():
|
||||||
|
to_insert_data.append((
|
||||||
|
frappe.generate_hash("", 10),
|
||||||
|
item_code,
|
||||||
|
'Item',
|
||||||
|
'item_defaults',
|
||||||
|
company,
|
||||||
|
item_default_data.get('default_warehouse'),
|
||||||
|
item_default_data.get('expense_account'),
|
||||||
|
item_default_data.get('income_account'),
|
||||||
|
item_default_data.get('buying_cost_center'),
|
||||||
|
item_default_data.get('selling_cost_center'),
|
||||||
|
))
|
||||||
|
|
||||||
|
if to_insert_data:
|
||||||
|
frappe.db.sql('''
|
||||||
|
INSERT INTO `tabItem Default`
|
||||||
|
(
|
||||||
|
`name`, `parent`, `parenttype`, `parentfield`, `company`, `default_warehouse`,
|
||||||
|
`expense_account`, `income_account`, `buying_cost_center`, `selling_cost_center`
|
||||||
|
)
|
||||||
|
VALUES {}
|
||||||
|
'''.format(', '.join(['%s'] * len(to_insert_data))), tuple(to_insert_data))
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# Copyright (c) 2018, Frappe and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
'''
|
||||||
|
default supplier was not set in the item defaults for multi company instance,
|
||||||
|
this patch will set the default supplier
|
||||||
|
|
||||||
|
'''
|
||||||
|
if not frappe.db.has_column('Item', 'default_supplier'):
|
||||||
|
return
|
||||||
|
|
||||||
|
frappe.reload_doc('stock', 'doctype', 'item_default')
|
||||||
|
frappe.reload_doc('stock', 'doctype', 'item')
|
||||||
|
|
||||||
|
companies = frappe.get_all("Company")
|
||||||
|
if len(companies) > 1:
|
||||||
|
frappe.db.sql(""" UPDATE `tabItem Default`, `tabItem`
|
||||||
|
SET `tabItem Default`.default_supplier = `tabItem`.default_supplier
|
||||||
|
WHERE
|
||||||
|
`tabItem Default`.parent = `tabItem`.name and `tabItem Default`.default_supplier is null
|
||||||
|
and `tabItem`.default_supplier is not null and `tabItem`.default_supplier != '' """)
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Copyright (c) 2019, Frappe and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from frappe.model.utils.rename_field import rename_field
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
frappe.reload_doc("setup", "doctype", "company")
|
||||||
|
if frappe.db.has_column('Company', 'default_terms'):
|
||||||
|
rename_field('Company', "default_terms", "default_selling_terms")
|
||||||
|
|
||||||
|
for company in frappe.get_all("Company", ["name", "default_selling_terms", "default_buying_terms"]):
|
||||||
|
if company.default_selling_terms and not company.default_buying_terms:
|
||||||
|
frappe.db.set_value("Company", company.name, "default_buying_terms", company.default_selling_terms)
|
||||||
|
|
||||||
|
frappe.reload_doc("setup", "doctype", "terms_and_conditions")
|
||||||
|
frappe.db.sql("update `tabTerms and Conditions` set selling=1, buying=1, hr=1")
|
||||||
@@ -3,6 +3,8 @@ import frappe
|
|||||||
from erpnext.regional.india.setup import make_custom_fields
|
from erpnext.regional.india.setup import make_custom_fields
|
||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
|
frappe.reload_doc("accounts", "doctype", "tax_category")
|
||||||
|
frappe.reload_doc("stock", "doctype", "item_manufacturer")
|
||||||
company = frappe.get_all('Company', filters = {'country': 'India'})
|
company = frappe.get_all('Company', filters = {'country': 'India'})
|
||||||
if not company:
|
if not company:
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -2,10 +2,9 @@ import frappe
|
|||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
frappe.reload_doctype('Task')
|
frappe.reload_doctype('Task')
|
||||||
frappe.reload_doctype('Project Task')
|
|
||||||
|
|
||||||
# add "Completed" if customized
|
# add "Completed" if customized
|
||||||
for doctype in ('Task', 'Project Task'):
|
for doctype in ('Task'):
|
||||||
property_setter_name = frappe.db.exists('Property Setter', dict(doc_type = doctype, field_name = 'status', property = 'options'))
|
property_setter_name = frappe.db.exists('Property Setter', dict(doc_type = doctype, field_name = 'status', property = 'options'))
|
||||||
if property_setter_name:
|
if property_setter_name:
|
||||||
property_setter = frappe.get_doc('Property Setter', property_setter_name)
|
property_setter = frappe.get_doc('Property Setter', property_setter_name)
|
||||||
|
|||||||
17
erpnext/patches/v12_0/update_due_date_in_gle.py
Normal file
17
erpnext/patches/v12_0/update_due_date_in_gle.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
frappe.reload_doc("accounts", "doctype", "gl_entry")
|
||||||
|
|
||||||
|
for doctype in ["Sales Invoice", "Purchase Invoice", "Journal Entry"]:
|
||||||
|
frappe.reload_doc("accounts", "doctype", frappe.scrub(doctype))
|
||||||
|
|
||||||
|
frappe.db.sql(""" UPDATE `tabGL Entry`, `tab{doctype}`
|
||||||
|
SET
|
||||||
|
`tabGL Entry`.due_date = `tab{doctype}`.due_date
|
||||||
|
WHERE
|
||||||
|
`tabGL Entry`.voucher_no = `tab{doctype}`.name and `tabGL Entry`.party is not null
|
||||||
|
and `tabGL Entry`.voucher_type in ('Sales Invoice', 'Purchase Invoice', 'Journal Entry')
|
||||||
|
and account in (select name from `tabAccount` where account_type in ('Receivable', 'Payable') )""" #nosec
|
||||||
|
.format(doctype=doctype))
|
||||||
@@ -110,4 +110,4 @@ def build_cache(item_code):
|
|||||||
def enqueue_build_cache(item_code):
|
def enqueue_build_cache(item_code):
|
||||||
if frappe.cache().hget('item_cache_build_in_progress', item_code):
|
if frappe.cache().hget('item_cache_build_in_progress', item_code):
|
||||||
return
|
return
|
||||||
frappe.enqueue(build_cache, item_code=item_code, queue='short')
|
frappe.enqueue(build_cache, item_code=item_code, queue='long')
|
||||||
|
|||||||
@@ -1,23 +1,6 @@
|
|||||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
// License: GNU General Public License v3. See license.txt
|
// License: GNU General Public License v3. See license.txt
|
||||||
frappe.ui.form.on("Project", {
|
frappe.ui.form.on("Project", {
|
||||||
setup: function (frm) {
|
|
||||||
frm.set_indicator_formatter('title',
|
|
||||||
function (doc) {
|
|
||||||
let indicator = 'orange';
|
|
||||||
if (doc.status == 'Overdue') {
|
|
||||||
indicator = 'red';
|
|
||||||
} else if (doc.status == 'Cancelled') {
|
|
||||||
indicator = 'dark grey';
|
|
||||||
} else if (doc.status == 'Completed') {
|
|
||||||
indicator = 'green';
|
|
||||||
}
|
|
||||||
return indicator;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
onload: function (frm) {
|
onload: function (frm) {
|
||||||
var so = frappe.meta.get_docfield("Project", "sales_order");
|
var so = frappe.meta.get_docfield("Project", "sales_order");
|
||||||
so.get_route_options_for_new_doc = function (field) {
|
so.get_route_options_for_new_doc = function (field) {
|
||||||
@@ -99,58 +82,4 @@ frappe.ui.form.on("Project", {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
tasks_refresh: function (frm) {
|
|
||||||
var grid = frm.get_field('tasks').grid;
|
|
||||||
grid.wrapper.find('select[data-fieldname="status"]').each(function () {
|
|
||||||
if ($(this).val() === 'Open') {
|
|
||||||
$(this).addClass('input-indicator-open');
|
|
||||||
} else {
|
|
||||||
$(this).removeClass('input-indicator-open');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
status: function(frm) {
|
|
||||||
if (frm.doc.status === 'Cancelled') {
|
|
||||||
frappe.confirm(__('Set tasks in this project as cancelled?'), () => {
|
|
||||||
frm.doc.tasks = frm.doc.tasks.map(task => {
|
|
||||||
task.status = 'Cancelled';
|
|
||||||
return task;
|
|
||||||
});
|
|
||||||
frm.refresh_field('tasks');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
frappe.ui.form.on("Project Task", {
|
|
||||||
edit_task: function(frm, doctype, name) {
|
|
||||||
var doc = frappe.get_doc(doctype, name);
|
|
||||||
if(doc.task_id) {
|
|
||||||
frappe.set_route("Form", "Task", doc.task_id);
|
|
||||||
} else {
|
|
||||||
frappe.msgprint(__("Save the document first."));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
edit_timesheet: function(frm, cdt, cdn) {
|
|
||||||
var child = locals[cdt][cdn];
|
|
||||||
frappe.route_options = {"project": frm.doc.project_name, "task": child.task_id};
|
|
||||||
frappe.set_route("List", "Timesheet");
|
|
||||||
},
|
|
||||||
|
|
||||||
make_timesheet: function(frm, cdt, cdn) {
|
|
||||||
var child = locals[cdt][cdn];
|
|
||||||
frappe.model.with_doctype('Timesheet', function() {
|
|
||||||
var doc = frappe.model.get_new_doc('Timesheet');
|
|
||||||
var row = frappe.model.add_child(doc, 'time_logs');
|
|
||||||
row.project = frm.doc.project_name;
|
|
||||||
row.task = child.task_id;
|
|
||||||
frappe.set_route('Form', doc.doctype, doc.name);
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
status: function(frm, doctype, name) {
|
|
||||||
frm.trigger('tasks_refresh');
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -19,10 +19,6 @@ class Project(Document):
|
|||||||
return '{0}: {1}'.format(_(self.status), frappe.safe_decode(self.project_name))
|
return '{0}: {1}'.format(_(self.status), frappe.safe_decode(self.project_name))
|
||||||
|
|
||||||
def onload(self):
|
def onload(self):
|
||||||
"""Load project tasks for quick view"""
|
|
||||||
if not self.get('__unsaved') and not self.get("tasks"):
|
|
||||||
self.load_tasks()
|
|
||||||
|
|
||||||
self.set_onload('activity_summary', frappe.db.sql('''select activity_type,
|
self.set_onload('activity_summary', frappe.db.sql('''select activity_type,
|
||||||
sum(hours) as total_hours
|
sum(hours) as total_hours
|
||||||
from `tabTimesheet Detail` where project=%s and docstatus < 2 group by activity_type
|
from `tabTimesheet Detail` where project=%s and docstatus < 2 group by activity_type
|
||||||
@@ -33,57 +29,19 @@ class Project(Document):
|
|||||||
def before_print(self):
|
def before_print(self):
|
||||||
self.onload()
|
self.onload()
|
||||||
|
|
||||||
def load_tasks(self):
|
|
||||||
"""Load `tasks` from the database"""
|
|
||||||
if frappe.flags.in_import:
|
|
||||||
return
|
|
||||||
project_task_custom_fields = frappe.get_all("Custom Field", {"dt": "Project Task"}, "fieldname")
|
|
||||||
|
|
||||||
self.tasks = []
|
|
||||||
for task in self.get_tasks():
|
|
||||||
task_map = {
|
|
||||||
"title": task.subject,
|
|
||||||
"status": task.status,
|
|
||||||
"start_date": task.exp_start_date,
|
|
||||||
"end_date": task.exp_end_date,
|
|
||||||
"description": task.description,
|
|
||||||
"task_id": task.name,
|
|
||||||
"task_weight": task.task_weight
|
|
||||||
}
|
|
||||||
|
|
||||||
self.map_custom_fields(task, task_map, project_task_custom_fields)
|
|
||||||
|
|
||||||
self.append("tasks", task_map)
|
|
||||||
|
|
||||||
def get_tasks(self):
|
|
||||||
if self.name is None:
|
|
||||||
return {}
|
|
||||||
else:
|
|
||||||
filters = {"project": self.name}
|
|
||||||
|
|
||||||
if self.get("deleted_task_list"):
|
|
||||||
filters.update({
|
|
||||||
'name': ("not in", self.deleted_task_list)
|
|
||||||
})
|
|
||||||
|
|
||||||
return frappe.get_all("Task", "*", filters, order_by="exp_start_date asc, status asc")
|
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_weights()
|
|
||||||
self.sync_tasks()
|
|
||||||
self.tasks = []
|
|
||||||
self.load_tasks()
|
|
||||||
if not self.is_new():
|
if not self.is_new():
|
||||||
self.copy_from_template()
|
self.copy_from_template()
|
||||||
self.validate_dates()
|
|
||||||
self.send_welcome_email()
|
self.send_welcome_email()
|
||||||
self.update_percent_complete(from_validate=True)
|
self.update_costing()
|
||||||
|
self.update_percent_complete()
|
||||||
|
|
||||||
def copy_from_template(self):
|
def copy_from_template(self):
|
||||||
'''
|
'''
|
||||||
Copy tasks from template
|
Copy tasks from template
|
||||||
'''
|
'''
|
||||||
if self.project_template and not len(self.tasks or []):
|
if self.project_template and not frappe.db.get_all('Task', dict(project = self.name), limit=1):
|
||||||
|
|
||||||
# has a template, and no loaded tasks, so lets create
|
# has a template, and no loaded tasks, so lets create
|
||||||
if not self.expected_start_date:
|
if not self.expected_start_date:
|
||||||
@@ -108,104 +66,6 @@ class Project(Document):
|
|||||||
task_weight = task.task_weight
|
task_weight = task.task_weight
|
||||||
)).insert()
|
)).insert()
|
||||||
|
|
||||||
# reload tasks after project
|
|
||||||
self.load_tasks()
|
|
||||||
|
|
||||||
def validate_dates(self):
|
|
||||||
if self.tasks:
|
|
||||||
for d in self.tasks:
|
|
||||||
if self.expected_start_date:
|
|
||||||
if d.start_date and getdate(d.start_date) < getdate(self.expected_start_date):
|
|
||||||
frappe.throw(_("Start date of task <b>{0}</b> cannot be less than <b>{1}</b> expected start date <b>{2}</b>")
|
|
||||||
.format(d.title, self.name, self.expected_start_date))
|
|
||||||
if d.end_date and getdate(d.end_date) < getdate(self.expected_start_date):
|
|
||||||
frappe.throw(_("End date of task <b>{0}</b> cannot be less than <b>{1}</b> expected start date <b>{2}</b>")
|
|
||||||
.format(d.title, self.name, self.expected_start_date))
|
|
||||||
|
|
||||||
if self.expected_end_date:
|
|
||||||
if d.start_date and getdate(d.start_date) > getdate(self.expected_end_date):
|
|
||||||
frappe.throw(_("Start date of task <b>{0}</b> cannot be greater than <b>{1}</b> expected end date <b>{2}</b>")
|
|
||||||
.format(d.title, self.name, self.expected_end_date))
|
|
||||||
if d.end_date and getdate(d.end_date) > getdate(self.expected_end_date):
|
|
||||||
frappe.throw(_("End date of task <b>{0}</b> cannot be greater than <b>{1}</b> expected end date <b>{2}</b>")
|
|
||||||
.format(d.title, self.name, self.expected_end_date))
|
|
||||||
|
|
||||||
if self.expected_start_date and self.expected_end_date:
|
|
||||||
if getdate(self.expected_end_date) < getdate(self.expected_start_date):
|
|
||||||
frappe.throw(_("Expected End Date can not be less than Expected Start Date"))
|
|
||||||
|
|
||||||
def validate_weights(self):
|
|
||||||
for task in self.tasks:
|
|
||||||
if task.task_weight is not None:
|
|
||||||
if task.task_weight < 0:
|
|
||||||
frappe.throw(_("Task weight cannot be negative"))
|
|
||||||
|
|
||||||
def sync_tasks(self):
|
|
||||||
"""sync tasks and remove table"""
|
|
||||||
if not hasattr(self, "deleted_task_list"):
|
|
||||||
self.set("deleted_task_list", [])
|
|
||||||
|
|
||||||
if self.flags.dont_sync_tasks: return
|
|
||||||
task_names = []
|
|
||||||
|
|
||||||
existing_task_data = {}
|
|
||||||
|
|
||||||
fields = ["title", "status", "start_date", "end_date", "description", "task_weight", "task_id"]
|
|
||||||
exclude_fieldtype = ["Button", "Column Break",
|
|
||||||
"Section Break", "Table", "Read Only", "Attach", "Attach Image", "Color", "Geolocation", "HTML", "Image"]
|
|
||||||
|
|
||||||
custom_fields = frappe.get_all("Custom Field", {"dt": "Project Task",
|
|
||||||
"fieldtype": ("not in", exclude_fieldtype)}, "fieldname")
|
|
||||||
|
|
||||||
for d in custom_fields:
|
|
||||||
fields.append(d.fieldname)
|
|
||||||
|
|
||||||
for d in frappe.get_all('Project Task',
|
|
||||||
fields = fields,
|
|
||||||
filters = {'parent': self.name}):
|
|
||||||
existing_task_data.setdefault(d.task_id, d)
|
|
||||||
|
|
||||||
for t in self.tasks:
|
|
||||||
if t.task_id:
|
|
||||||
task = frappe.get_doc("Task", t.task_id)
|
|
||||||
else:
|
|
||||||
task = frappe.new_doc("Task")
|
|
||||||
task.project = self.name
|
|
||||||
|
|
||||||
if not t.task_id or self.is_row_updated(t, existing_task_data, fields):
|
|
||||||
task.update({
|
|
||||||
"subject": t.title,
|
|
||||||
"status": t.status,
|
|
||||||
"exp_start_date": t.start_date,
|
|
||||||
"exp_end_date": t.end_date,
|
|
||||||
"description": t.description,
|
|
||||||
"task_weight": t.task_weight
|
|
||||||
})
|
|
||||||
|
|
||||||
self.map_custom_fields(t, task, custom_fields)
|
|
||||||
|
|
||||||
task.flags.ignore_links = True
|
|
||||||
task.flags.from_project = True
|
|
||||||
task.flags.ignore_feed = True
|
|
||||||
|
|
||||||
if t.task_id:
|
|
||||||
task.update({
|
|
||||||
"modified_by": frappe.session.user,
|
|
||||||
"modified": now()
|
|
||||||
})
|
|
||||||
|
|
||||||
task.run_method("validate")
|
|
||||||
task.db_update()
|
|
||||||
else:
|
|
||||||
task.save(ignore_permissions = True)
|
|
||||||
task_names.append(task.name)
|
|
||||||
else:
|
|
||||||
task_names.append(task.name)
|
|
||||||
|
|
||||||
# delete
|
|
||||||
for t in frappe.get_all("Task", ["name"], {"project": self.name, "name": ("not in", task_names)}):
|
|
||||||
self.deleted_task_list.append(t.name)
|
|
||||||
|
|
||||||
def is_row_updated(self, row, existing_task_data, fields):
|
def is_row_updated(self, row, existing_task_data, fields):
|
||||||
if self.get("__islocal") or not existing_task_data: return True
|
if self.get("__islocal") or not existing_task_data: return True
|
||||||
|
|
||||||
@@ -215,48 +75,43 @@ class Project(Document):
|
|||||||
if row.get(field) != d.get(field):
|
if row.get(field) != d.get(field):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def map_custom_fields(self, source, target, custom_fields):
|
|
||||||
for field in custom_fields:
|
|
||||||
target.update({
|
|
||||||
field.fieldname: source.get(field.fieldname)
|
|
||||||
})
|
|
||||||
|
|
||||||
def update_project(self):
|
def update_project(self):
|
||||||
|
'''Called externally by Task'''
|
||||||
self.update_percent_complete()
|
self.update_percent_complete()
|
||||||
self.update_costing()
|
self.update_costing()
|
||||||
|
self.db_update()
|
||||||
|
|
||||||
def after_insert(self):
|
def after_insert(self):
|
||||||
self.copy_from_template()
|
self.copy_from_template()
|
||||||
if self.sales_order:
|
if self.sales_order:
|
||||||
frappe.db.set_value("Sales Order", self.sales_order, "project", self.name)
|
frappe.db.set_value("Sales Order", self.sales_order, "project", self.name)
|
||||||
|
|
||||||
def update_percent_complete(self, from_validate=False):
|
def update_percent_complete(self):
|
||||||
if not self.tasks: return
|
total = frappe.db.count('Task', dict(project=self.name))
|
||||||
total = frappe.db.sql("""select count(name) from tabTask where project=%s""", self.name)[0][0]
|
|
||||||
|
|
||||||
if not total and self.percent_complete:
|
if not total:
|
||||||
self.percent_complete = 0
|
self.percent_complete = 0
|
||||||
|
else:
|
||||||
|
if (self.percent_complete_method == "Task Completion" and total > 0) or (
|
||||||
|
not self.percent_complete_method and total > 0):
|
||||||
|
completed = frappe.db.sql("""select count(name) from tabTask where
|
||||||
|
project=%s and status in ('Cancelled', 'Completed')""", self.name)[0][0]
|
||||||
|
self.percent_complete = flt(flt(completed) / total * 100, 2)
|
||||||
|
|
||||||
if (self.percent_complete_method == "Task Completion" and total > 0) or (
|
if (self.percent_complete_method == "Task Progress" and total > 0):
|
||||||
not self.percent_complete_method and total > 0):
|
progress = frappe.db.sql("""select sum(progress) from tabTask where
|
||||||
completed = frappe.db.sql("""select count(name) from tabTask where
|
project=%s""", self.name)[0][0]
|
||||||
project=%s and status in ('Cancelled', 'Completed')""", self.name)[0][0]
|
self.percent_complete = flt(flt(progress) / total, 2)
|
||||||
self.percent_complete = flt(flt(completed) / total * 100, 2)
|
|
||||||
|
|
||||||
if (self.percent_complete_method == "Task Progress" and total > 0):
|
if (self.percent_complete_method == "Task Weight" and total > 0):
|
||||||
progress = frappe.db.sql("""select sum(progress) from tabTask where
|
weight_sum = frappe.db.sql("""select sum(task_weight) from tabTask where
|
||||||
project=%s""", self.name)[0][0]
|
project=%s""", self.name)[0][0]
|
||||||
self.percent_complete = flt(flt(progress) / total, 2)
|
weighted_progress = frappe.db.sql("""select progress, task_weight from tabTask where
|
||||||
|
project=%s""", self.name, as_dict=1)
|
||||||
if (self.percent_complete_method == "Task Weight" and total > 0):
|
pct_complete = 0
|
||||||
weight_sum = frappe.db.sql("""select sum(task_weight) from tabTask where
|
for row in weighted_progress:
|
||||||
project=%s""", self.name)[0][0]
|
pct_complete += row["progress"] * frappe.utils.safe_div(row["task_weight"], weight_sum)
|
||||||
weighted_progress = frappe.db.sql("""select progress, task_weight from tabTask where
|
self.percent_complete = flt(flt(pct_complete), 2)
|
||||||
project=%s""", self.name, as_dict=1)
|
|
||||||
pct_complete = 0
|
|
||||||
for row in weighted_progress:
|
|
||||||
pct_complete += row["progress"] * frappe.utils.safe_div(row["task_weight"], weight_sum)
|
|
||||||
self.percent_complete = flt(flt(pct_complete), 2)
|
|
||||||
|
|
||||||
# don't update status if it is cancelled
|
# don't update status if it is cancelled
|
||||||
if self.status == 'Cancelled':
|
if self.status == 'Cancelled':
|
||||||
@@ -268,9 +123,6 @@ class Project(Document):
|
|||||||
else:
|
else:
|
||||||
self.status = "Open"
|
self.status = "Open"
|
||||||
|
|
||||||
if not from_validate:
|
|
||||||
self.db_update()
|
|
||||||
|
|
||||||
def update_costing(self):
|
def update_costing(self):
|
||||||
from_time_sheet = frappe.db.sql("""select
|
from_time_sheet = frappe.db.sql("""select
|
||||||
sum(costing_amount) as costing_amount,
|
sum(costing_amount) as costing_amount,
|
||||||
@@ -297,7 +149,6 @@ class Project(Document):
|
|||||||
self.update_sales_amount()
|
self.update_sales_amount()
|
||||||
self.update_billed_amount()
|
self.update_billed_amount()
|
||||||
self.calculate_gross_margin()
|
self.calculate_gross_margin()
|
||||||
self.db_update()
|
|
||||||
|
|
||||||
def calculate_gross_margin(self):
|
def calculate_gross_margin(self):
|
||||||
expense_amount = (flt(self.total_costing_amount) + flt(self.total_expense_claim)
|
expense_amount = (flt(self.total_costing_amount) + flt(self.total_expense_claim)
|
||||||
@@ -348,57 +199,6 @@ class Project(Document):
|
|||||||
content=content.format(*messages))
|
content=content.format(*messages))
|
||||||
user.welcome_email_sent = 1
|
user.welcome_email_sent = 1
|
||||||
|
|
||||||
def on_update(self):
|
|
||||||
self.delete_task()
|
|
||||||
self.load_tasks()
|
|
||||||
self.update_project()
|
|
||||||
self.update_dependencies_on_duplicated_project()
|
|
||||||
|
|
||||||
def delete_task(self):
|
|
||||||
if not self.get('deleted_task_list'): return
|
|
||||||
|
|
||||||
for d in self.get('deleted_task_list'):
|
|
||||||
# unlink project
|
|
||||||
frappe.db.set_value('Task', d, 'project', '')
|
|
||||||
|
|
||||||
self.deleted_task_list = []
|
|
||||||
|
|
||||||
def update_dependencies_on_duplicated_project(self):
|
|
||||||
if self.flags.dont_sync_tasks: return
|
|
||||||
if not self.copied_from:
|
|
||||||
self.copied_from = self.name
|
|
||||||
|
|
||||||
if self.name != self.copied_from and self.get('__unsaved'):
|
|
||||||
# duplicated project
|
|
||||||
dependency_map = {}
|
|
||||||
for task in self.tasks:
|
|
||||||
_task = frappe.db.get_value(
|
|
||||||
'Task',
|
|
||||||
{"subject": task.title, "project": self.copied_from},
|
|
||||||
['name', 'depends_on_tasks'],
|
|
||||||
as_dict=True
|
|
||||||
)
|
|
||||||
|
|
||||||
if _task is None:
|
|
||||||
continue
|
|
||||||
|
|
||||||
name = _task.name
|
|
||||||
|
|
||||||
dependency_map[task.title] = [x['subject'] for x in frappe.get_list(
|
|
||||||
'Task Depends On', {"parent": name}, ['subject'])]
|
|
||||||
|
|
||||||
for key, value in iteritems(dependency_map):
|
|
||||||
task_name = frappe.db.get_value('Task', {"subject": key, "project": self.name })
|
|
||||||
|
|
||||||
task_doc = frappe.get_doc('Task', task_name)
|
|
||||||
|
|
||||||
for dt in value:
|
|
||||||
dt_name = frappe.db.get_value('Task', {"subject": dt, "project": self.name})
|
|
||||||
task_doc.append('depends_on', {"task": dt_name})
|
|
||||||
|
|
||||||
task_doc.db_update()
|
|
||||||
|
|
||||||
|
|
||||||
def get_timeline_data(doctype, name):
|
def get_timeline_data(doctype, name):
|
||||||
'''Return timeline for attendance'''
|
'''Return timeline for attendance'''
|
||||||
return dict(frappe.db.sql('''select unix_timestamp(from_time), count(*)
|
return dict(frappe.db.sql('''select unix_timestamp(from_time), count(*)
|
||||||
|
|||||||
@@ -19,18 +19,18 @@ class TestProject(unittest.TestCase):
|
|||||||
|
|
||||||
project = get_project('Test Project with Template')
|
project = get_project('Test Project with Template')
|
||||||
|
|
||||||
project.load_tasks()
|
tasks = frappe.get_all('Task', '*', dict(project=project.name), order_by='creation asc')
|
||||||
|
|
||||||
task1 = project.tasks[0]
|
task1 = tasks[0]
|
||||||
self.assertEqual(task1.title, 'Task 1')
|
self.assertEqual(task1.subject, 'Task 1')
|
||||||
self.assertEqual(task1.description, 'Task 1 description')
|
self.assertEqual(task1.description, 'Task 1 description')
|
||||||
self.assertEqual(getdate(task1.start_date), getdate('2019-01-01'))
|
self.assertEqual(getdate(task1.exp_start_date), getdate('2019-01-01'))
|
||||||
self.assertEqual(getdate(task1.end_date), getdate('2019-01-04'))
|
self.assertEqual(getdate(task1.exp_end_date), getdate('2019-01-04'))
|
||||||
|
|
||||||
self.assertEqual(len(project.tasks), 4)
|
self.assertEqual(len(tasks), 4)
|
||||||
task4 = project.tasks[3]
|
task4 = tasks[3]
|
||||||
self.assertEqual(task4.title, 'Task 4')
|
self.assertEqual(task4.subject, 'Task 4')
|
||||||
self.assertEqual(getdate(task4.end_date), getdate('2019-01-06'))
|
self.assertEqual(getdate(task4.exp_end_date), getdate('2019-01-06'))
|
||||||
|
|
||||||
def get_project(name):
|
def get_project(name):
|
||||||
template = get_project_template()
|
template = get_project_template()
|
||||||
|
|||||||
@@ -1,12 +1,6 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"project_name": "_Test Project",
|
"project_name": "_Test Project",
|
||||||
"status": "Open",
|
"status": "Open"
|
||||||
"tasks":[
|
|
||||||
{
|
|
||||||
"title": "_Test Task",
|
|
||||||
"status": "Open"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -1,430 +0,0 @@
|
|||||||
{
|
|
||||||
"allow_copy": 0,
|
|
||||||
"allow_events_in_timeline": 0,
|
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"beta": 0,
|
|
||||||
"creation": "2015-02-22 11:15:28.201059",
|
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
|
||||||
"document_type": "Other",
|
|
||||||
"editable_grid": 1,
|
|
||||||
"engine": "InnoDB",
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 3,
|
|
||||||
"fieldname": "title",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"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": "Title",
|
|
||||||
"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": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 3,
|
|
||||||
"default": "Open",
|
|
||||||
"fieldname": "status",
|
|
||||||
"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": "Status",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
|
||||||
"options": "Open\nWorking\nPending Review\nOverdue\nCompleted\nCancelled",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"depends_on": "task_id",
|
|
||||||
"fieldname": "edit_task",
|
|
||||||
"fieldtype": "Button",
|
|
||||||
"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": "View Task",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "edit_timesheet",
|
|
||||||
"fieldtype": "Button",
|
|
||||||
"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": "View Timesheet",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "make_timesheet",
|
|
||||||
"fieldtype": "Button",
|
|
||||||
"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": "Make Timesheet",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break_6",
|
|
||||||
"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,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 2,
|
|
||||||
"fieldname": "start_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": "Start 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,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 2,
|
|
||||||
"default": "",
|
|
||||||
"fieldname": "end_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": "End 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,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "task_weight",
|
|
||||||
"fieldtype": "Float",
|
|
||||||
"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": "Weight",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 1,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"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,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "description",
|
|
||||||
"fieldtype": "Text Editor",
|
|
||||||
"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,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "task_id",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"hidden": 1,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Task ID",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
|
||||||
"options": "Task",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 1,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 1,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"has_web_view": 0,
|
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 0,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 1,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2019-02-19 12:30:52.648868",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Projects",
|
|
||||||
"name": "Project Task",
|
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [],
|
|
||||||
"quick_entry": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
|
||||||
"sort_order": "DESC",
|
|
||||||
"track_changes": 0,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors
|
|
||||||
# For license information, please see license.txt
|
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import frappe
|
|
||||||
from frappe.model.document import Document
|
|
||||||
|
|
||||||
class ProjectTask(Document):
|
|
||||||
pass
|
|
||||||
@@ -158,12 +158,6 @@ class Task(NestedSet):
|
|||||||
if check_if_child_exists(self.name):
|
if check_if_child_exists(self.name):
|
||||||
throw(_("Child Task exists for this Task. You can not delete this Task."))
|
throw(_("Child Task exists for this Task. You can not delete this Task."))
|
||||||
|
|
||||||
if self.project:
|
|
||||||
tasks = frappe.get_doc('Project', self.project).tasks
|
|
||||||
for task in tasks:
|
|
||||||
if task.get('task_id') == self.name:
|
|
||||||
frappe.delete_doc('Project Task', task.name)
|
|
||||||
|
|
||||||
self.update_nsm_model()
|
self.update_nsm_model()
|
||||||
|
|
||||||
def update_status(self):
|
def update_status(self):
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ class CallPopup {
|
|||||||
});
|
});
|
||||||
wrapper.append(`
|
wrapper.append(`
|
||||||
<div class="caller-info flex">
|
<div class="caller-info flex">
|
||||||
${frappe.avatar(null, 'avatar-xl', contact.name, contact.image)}
|
${frappe.avatar(null, 'avatar-xl', contact.name, contact.image || '')}
|
||||||
<div>
|
<div>
|
||||||
<h5>${contact_name}</h5>
|
<h5>${contact_name}</h5>
|
||||||
<div>${contact.mobile_no || ''}</div>
|
<div>${contact.mobile_no || ''}</div>
|
||||||
|
|||||||
@@ -61,6 +61,14 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(this.frm.fields_dict.tc_name) {
|
||||||
|
this.frm.set_query("tc_name", function() {
|
||||||
|
return{
|
||||||
|
filters: { 'buying': 1 }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
me.frm.set_query('supplier', erpnext.queries.supplier);
|
me.frm.set_query('supplier', erpnext.queries.supplier);
|
||||||
me.frm.set_query('contact_person', erpnext.queries.contact_query);
|
me.frm.set_query('contact_person', erpnext.queries.contact_query);
|
||||||
me.frm.set_query('supplier_address', erpnext.queries.address_query);
|
me.frm.set_query('supplier_address', erpnext.queries.address_query);
|
||||||
|
|||||||
@@ -383,8 +383,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
|
|
||||||
setup_sms: function() {
|
setup_sms: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
|
let blacklist = ['Purchase Invoice', 'BOM'];
|
||||||
if(this.frm.doc.docstatus===1 && !in_list(["Lost", "Stopped", "Closed"], this.frm.doc.status)
|
if(this.frm.doc.docstatus===1 && !in_list(["Lost", "Stopped", "Closed"], this.frm.doc.status)
|
||||||
&& this.frm.doctype != "Purchase Invoice") {
|
&& !blacklist.includes(this.frm.doctype)) {
|
||||||
this.frm.page.add_menu_item(__('Send SMS'), function() { me.send_sms(); });
|
this.frm.page.add_menu_item(__('Send SMS'), function() { me.send_sms(); });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -584,8 +585,17 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
me.frm.set_value("letter_head", company_doc.default_letter_head);
|
me.frm.set_value("letter_head", company_doc.default_letter_head);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (company_doc.default_terms && me.frm.doc.doctype != "Purchase Invoice" && frappe.meta.has_field(me.frm.doc.doctype, "tc_name")) {
|
let selling_doctypes_for_tc = ["Sales Invoice", "Quotation", "Sales Order", "Delivery Note"];
|
||||||
me.frm.set_value("tc_name", company_doc.default_terms);
|
if (company_doc.default_selling_terms && frappe.meta.has_field(me.frm.doc.doctype, "tc_name") &&
|
||||||
|
selling_doctypes_for_tc.indexOf(me.frm.doc.doctype) != -1) {
|
||||||
|
me.frm.set_value("tc_name", company_doc.default_selling_terms);
|
||||||
|
}
|
||||||
|
let buying_doctypes_for_tc = ["Request for Quotation", "Supplier Quotation", "Purchase Order",
|
||||||
|
"Material Request", "Purchase Receipt"];
|
||||||
|
// Purchase Invoice is excluded as per issue #3345
|
||||||
|
if (company_doc.default_buying_terms && frappe.meta.has_field(me.frm.doc.doctype, "tc_name") &&
|
||||||
|
buying_doctypes_for_tc.indexOf(me.frm.doc.doctype) != -1) {
|
||||||
|
me.frm.set_value("tc_name", company_doc.default_buying_terms);
|
||||||
}
|
}
|
||||||
|
|
||||||
frappe.run_serially([
|
frappe.run_serially([
|
||||||
|
|||||||
@@ -129,9 +129,7 @@ function get_filters(){
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
let dimension_filters = erpnext.get_dimension_filters();
|
erpnext.dimension_filters.then((dimensions) => {
|
||||||
|
|
||||||
dimension_filters.then((dimensions) => {
|
|
||||||
dimensions.forEach((dimension) => {
|
dimensions.forEach((dimension) => {
|
||||||
filters.push({
|
filters.push({
|
||||||
"fieldname": dimension["fieldname"],
|
"fieldname": dimension["fieldname"],
|
||||||
|
|||||||
@@ -20,8 +20,10 @@ erpnext.SMSManager = function SMSManager(doc) {
|
|||||||
'Purchase Receipt' : 'Items has been received against purchase receipt: ' + doc.name
|
'Purchase Receipt' : 'Items has been received against purchase receipt: ' + doc.name
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_list(['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice'], doc.doctype))
|
if (in_list(['Sales Order', 'Delivery Note', 'Sales Invoice'], doc.doctype))
|
||||||
this.show(doc.contact_person, 'Customer', doc.customer, '', default_msg[doc.doctype]);
|
this.show(doc.contact_person, 'Customer', doc.customer, '', default_msg[doc.doctype]);
|
||||||
|
else if (doc.doctype === 'Quotation')
|
||||||
|
this.show(doc.contact_person, 'Customer', doc.party_name, '', default_msg[doc.doctype]);
|
||||||
else if (in_list(['Purchase Order', 'Purchase Receipt'], doc.doctype))
|
else if (in_list(['Purchase Order', 'Purchase Receipt'], doc.doctype))
|
||||||
this.show(doc.contact_person, 'Supplier', doc.supplier, '', default_msg[doc.doctype]);
|
this.show(doc.contact_person, 'Supplier', doc.supplier, '', default_msg[doc.doctype]);
|
||||||
else if (doc.doctype == 'Lead')
|
else if (doc.doctype == 'Lead')
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ erpnext.doctypes_with_dimensions = ["GL Entry", "Sales Invoice", "Purchase Invoi
|
|||||||
"Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation",
|
"Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation",
|
||||||
"Travel Request", "Fees", "POS Profile"];
|
"Travel Request", "Fees", "POS Profile"];
|
||||||
|
|
||||||
let dimension_filters = erpnext.get_dimension_filters();
|
erpnext.dimension_filters = erpnext.get_dimension_filters();
|
||||||
|
|
||||||
erpnext.doctypes_with_dimensions.forEach((doctype) => {
|
erpnext.doctypes_with_dimensions.forEach((doctype) => {
|
||||||
frappe.ui.form.on(doctype, {
|
frappe.ui.form.on(doctype, {
|
||||||
onload: function(frm) {
|
onload: function(frm) {
|
||||||
dimension_filters.then((dimensions) => {
|
erpnext.dimension_filters.then((dimensions) => {
|
||||||
dimensions.forEach((dimension) => {
|
dimensions.forEach((dimension) => {
|
||||||
frappe.model.with_doctype(dimension['document_type'], () => {
|
frappe.model.with_doctype(dimension['document_type'], () => {
|
||||||
if (frappe.meta.has_field(dimension['document_type'], 'is_group')) {
|
if (frappe.meta.has_field(dimension['document_type'], 'is_group')) {
|
||||||
|
|||||||
@@ -326,6 +326,9 @@ def get_company_country(company):
|
|||||||
return frappe.get_cached_value('Company', company, 'country')
|
return frappe.get_cached_value('Company', company, 'country')
|
||||||
|
|
||||||
def get_e_invoice_attachments(invoice):
|
def get_e_invoice_attachments(invoice):
|
||||||
|
if not invoice.company_tax_id:
|
||||||
|
return []
|
||||||
|
|
||||||
out = []
|
out = []
|
||||||
attachments = get_attachments(invoice.doctype, invoice.name)
|
attachments = get_attachments(invoice.doctype, invoice.name)
|
||||||
company_tax_id = invoice.company_tax_id if invoice.company_tax_id.startswith("IT") else "IT" + invoice.company_tax_id
|
company_tax_id = invoice.company_tax_id if invoice.company_tax_id.startswith("IT") else "IT" + invoice.company_tax_id
|
||||||
|
|||||||
@@ -74,7 +74,6 @@ class Gstr1Report(object):
|
|||||||
|
|
||||||
for inv, items_based_on_rate in self.items_based_on_tax_rate.items():
|
for inv, items_based_on_rate in self.items_based_on_tax_rate.items():
|
||||||
invoice_details = self.invoices.get(inv)
|
invoice_details = self.invoices.get(inv)
|
||||||
|
|
||||||
for rate, items in items_based_on_rate.items():
|
for rate, items in items_based_on_rate.items():
|
||||||
place_of_supply = invoice_details.get("place_of_supply")
|
place_of_supply = invoice_details.get("place_of_supply")
|
||||||
ecommerce_gstin = invoice_details.get("ecommerce_gstin")
|
ecommerce_gstin = invoice_details.get("ecommerce_gstin")
|
||||||
@@ -85,7 +84,7 @@ class Gstr1Report(object):
|
|||||||
"rate": "",
|
"rate": "",
|
||||||
"taxable_value": 0,
|
"taxable_value": 0,
|
||||||
"cess_amount": 0,
|
"cess_amount": 0,
|
||||||
"type": 0
|
"type": ""
|
||||||
})
|
})
|
||||||
|
|
||||||
row = b2cs_output.get((rate, place_of_supply, ecommerce_gstin))
|
row = b2cs_output.get((rate, place_of_supply, ecommerce_gstin))
|
||||||
@@ -94,6 +93,7 @@ class Gstr1Report(object):
|
|||||||
row["rate"] = rate
|
row["rate"] = rate
|
||||||
row["taxable_value"] += sum([abs(net_amount)
|
row["taxable_value"] += sum([abs(net_amount)
|
||||||
for item_code, net_amount in self.invoice_items.get(inv).items() if item_code in items])
|
for item_code, net_amount in self.invoice_items.get(inv).items() if item_code in items])
|
||||||
|
row["cess_amount"] += flt(self.invoice_cess.get(inv), 2)
|
||||||
row["type"] = "E" if ecommerce_gstin else "OE"
|
row["type"] = "E" if ecommerce_gstin else "OE"
|
||||||
|
|
||||||
for key, value in iteritems(b2cs_output):
|
for key, value in iteritems(b2cs_output):
|
||||||
@@ -123,6 +123,10 @@ class Gstr1Report(object):
|
|||||||
|
|
||||||
row += [tax_rate or 0, taxable_value]
|
row += [tax_rate or 0, taxable_value]
|
||||||
|
|
||||||
|
for column in self.other_columns:
|
||||||
|
if column.get('fieldname') == 'cess_amount':
|
||||||
|
row.append(flt(self.invoice_cess.get(invoice), 2))
|
||||||
|
|
||||||
return row, taxable_value
|
return row, taxable_value
|
||||||
|
|
||||||
def get_invoice_data(self):
|
def get_invoice_data(self):
|
||||||
@@ -327,7 +331,7 @@ class Gstr1Report(object):
|
|||||||
"fieldtype": "Data"
|
"fieldtype": "Data"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "invoice_type",
|
"fieldname": "gst_category",
|
||||||
"label": "Invoice Type",
|
"label": "Invoice Type",
|
||||||
"fieldtype": "Data"
|
"fieldtype": "Data"
|
||||||
},
|
},
|
||||||
@@ -564,12 +568,18 @@ def get_json():
|
|||||||
|
|
||||||
out = get_b2b_json(res, gstin)
|
out = get_b2b_json(res, gstin)
|
||||||
gst_json["b2b"] = out
|
gst_json["b2b"] = out
|
||||||
|
|
||||||
elif filters["type_of_business"] == "B2C Large":
|
elif filters["type_of_business"] == "B2C Large":
|
||||||
for item in report_data[:-1]:
|
for item in report_data[:-1]:
|
||||||
res.setdefault(item["place_of_supply"], []).append(item)
|
res.setdefault(item["place_of_supply"], []).append(item)
|
||||||
|
|
||||||
out = get_b2cl_json(res, gstin)
|
out = get_b2cl_json(res, gstin)
|
||||||
gst_json["b2cl"] = out
|
gst_json["b2cl"] = out
|
||||||
|
|
||||||
|
elif filters["type_of_business"] == "B2C Small":
|
||||||
|
out = get_b2cs_json(report_data[:-1], gstin)
|
||||||
|
gst_json["b2cs"] = out
|
||||||
|
|
||||||
elif filters["type_of_business"] == "EXPORT":
|
elif filters["type_of_business"] == "EXPORT":
|
||||||
for item in report_data[:-1]:
|
for item in report_data[:-1]:
|
||||||
res.setdefault(item["export_type"], []).append(item)
|
res.setdefault(item["export_type"], []).append(item)
|
||||||
@@ -605,6 +615,45 @@ def get_b2b_json(res, gstin):
|
|||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
def get_b2cs_json(data, gstin):
|
||||||
|
|
||||||
|
company_state_number = gstin[0:2]
|
||||||
|
|
||||||
|
out = []
|
||||||
|
for d in data:
|
||||||
|
|
||||||
|
pos = d.get('place_of_supply').split('-')[0]
|
||||||
|
tax_details = {}
|
||||||
|
|
||||||
|
rate = d.get('rate', 0)
|
||||||
|
tax = flt((d["taxable_value"]*rate)/100.0, 2)
|
||||||
|
|
||||||
|
if company_state_number == pos:
|
||||||
|
tax_details.update({"camt": flt(tax/2.0, 2), "samt": flt(tax/2.0, 2)})
|
||||||
|
else:
|
||||||
|
tax_details.update({"iamt": tax})
|
||||||
|
|
||||||
|
inv = {
|
||||||
|
"sply_ty": "INTRA" if company_state_number == pos else "INTER",
|
||||||
|
"pos": pos,
|
||||||
|
"typ": d.get('type'),
|
||||||
|
"txval": flt(d.get('taxable_value'), 2),
|
||||||
|
"rt": rate,
|
||||||
|
"iamt": flt(tax_details.get('iamt'), 2),
|
||||||
|
"camt": flt(tax_details.get('camt'), 2),
|
||||||
|
"samt": flt(tax_details.get('samt'), 2),
|
||||||
|
"csamt": flt(d.get('cess_amount'), 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.get('type') == "E" and d.get('ecommerce_gstin'):
|
||||||
|
inv.update({
|
||||||
|
"etin": d.get('ecommerce_gstin')
|
||||||
|
})
|
||||||
|
|
||||||
|
out.append(inv)
|
||||||
|
|
||||||
|
return out
|
||||||
|
|
||||||
def get_b2cl_json(res, gstin):
|
def get_b2cl_json(res, gstin):
|
||||||
out = []
|
out = []
|
||||||
for pos in res:
|
for pos in res:
|
||||||
|
|||||||
@@ -25,10 +25,6 @@ def get_data():
|
|||||||
'label': _('Orders'),
|
'label': _('Orders'),
|
||||||
'items': ['Sales Order', 'Delivery Note', 'Sales Invoice']
|
'items': ['Sales Order', 'Delivery Note', 'Sales Invoice']
|
||||||
},
|
},
|
||||||
{
|
|
||||||
'label': _('Service Level Agreement'),
|
|
||||||
'items': ['Service Level Agreement']
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
'label': _('Payments'),
|
'label': _('Payments'),
|
||||||
'items': ['Payment Entry']
|
'items': ['Payment Entry']
|
||||||
|
|||||||
@@ -107,7 +107,6 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
|||||||
refresh: function(doc, dt, dn) {
|
refresh: function(doc, dt, dn) {
|
||||||
var me = this;
|
var me = this;
|
||||||
this._super();
|
this._super();
|
||||||
var allow_purchase = false;
|
|
||||||
var allow_delivery = false;
|
var allow_delivery = false;
|
||||||
|
|
||||||
if(doc.docstatus==1) {
|
if(doc.docstatus==1) {
|
||||||
@@ -129,28 +128,9 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
|||||||
me.frm.cscript.update_status('Re-open', 'Draft')
|
me.frm.cscript.update_status('Re-open', 'Draft')
|
||||||
}, __("Status"));
|
}, __("Status"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(doc.status !== 'Closed') {
|
if(doc.status !== 'Closed') {
|
||||||
if(doc.status !== 'On Hold') {
|
if(doc.status !== 'On Hold') {
|
||||||
for (var i in this.frm.doc.items) {
|
|
||||||
var item = this.frm.doc.items[i];
|
|
||||||
if(item.delivered_by_supplier === 1 || item.supplier){
|
|
||||||
if(item.qty > flt(item.ordered_qty)
|
|
||||||
&& item.qty > flt(item.delivered_qty)) {
|
|
||||||
allow_purchase = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.delivered_by_supplier===0) {
|
|
||||||
if(item.qty > flt(item.delivered_qty)) {
|
|
||||||
allow_delivery = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (allow_delivery && allow_purchase) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.frm.has_perm("submit")) {
|
if (this.frm.has_perm("submit")) {
|
||||||
if(flt(doc.per_delivered, 6) < 100 || flt(doc.per_billed) < 100) {
|
if(flt(doc.per_delivered, 6) < 100 || flt(doc.per_billed) < 100) {
|
||||||
@@ -180,9 +160,8 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// make purchase order
|
// make purchase order
|
||||||
if(flt(doc.per_delivered, 6) < 100 && allow_purchase) {
|
|
||||||
this.frm.add_custom_button(__('Purchase Order'), () => this.make_purchase_order(), __('Create'));
|
this.frm.add_custom_button(__('Purchase Order'), () => this.make_purchase_order(), __('Create'));
|
||||||
}
|
|
||||||
// maintenance
|
// maintenance
|
||||||
if(flt(doc.per_delivered, 2) < 100 &&
|
if(flt(doc.per_delivered, 2) < 100 &&
|
||||||
["Sales", "Shopping Cart"].indexOf(doc.order_type)===-1) {
|
["Sales", "Shopping Cart"].indexOf(doc.order_type)===-1) {
|
||||||
@@ -543,6 +522,42 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
|||||||
filters: {'parent': me.frm.doc.name}
|
filters: {'parent': me.frm.doc.name}
|
||||||
}
|
}
|
||||||
}},
|
}},
|
||||||
|
{fieldname: 'items_for_po', fieldtype: 'Table', label: 'Select Items',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
fieldtype:'Data',
|
||||||
|
fieldname:'item_code',
|
||||||
|
label: __('Item'),
|
||||||
|
read_only:1,
|
||||||
|
in_list_view:1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype:'Data',
|
||||||
|
fieldname:'item_name',
|
||||||
|
label: __('Item name'),
|
||||||
|
read_only:1,
|
||||||
|
in_list_view:1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype:'Float',
|
||||||
|
fieldname:'qty',
|
||||||
|
label: __('Quantity'),
|
||||||
|
read_only: 1,
|
||||||
|
in_list_view:1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype:'Link',
|
||||||
|
read_only:1,
|
||||||
|
fieldname:'uom',
|
||||||
|
label: __('UOM'),
|
||||||
|
in_list_view:1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
data: cur_frm.doc.items,
|
||||||
|
get_data: function() {
|
||||||
|
return cur_frm.doc.items
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
{"fieldtype": "Button", "label": __('Create Purchase Order'), "fieldname": "make_purchase_order", "cssClass": "btn-primary"},
|
{"fieldtype": "Button", "label": __('Create Purchase Order'), "fieldname": "make_purchase_order", "cssClass": "btn-primary"},
|
||||||
]
|
]
|
||||||
@@ -550,13 +565,22 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
|||||||
|
|
||||||
dialog.fields_dict.make_purchase_order.$input.click(function() {
|
dialog.fields_dict.make_purchase_order.$input.click(function() {
|
||||||
var args = dialog.get_values();
|
var args = dialog.get_values();
|
||||||
|
let selected_items = dialog.fields_dict.items_for_po.grid.get_selected_children()
|
||||||
|
if(selected_items.length == 0) {
|
||||||
|
frappe.throw({message: 'Please select Item form Table', title: __('Message'), indicator:'blue'})
|
||||||
|
}
|
||||||
|
let selected_items_list = []
|
||||||
|
for(let i in selected_items){
|
||||||
|
selected_items_list.push(selected_items[i].item_code)
|
||||||
|
}
|
||||||
dialog.hide();
|
dialog.hide();
|
||||||
return frappe.call({
|
return frappe.call({
|
||||||
type: "GET",
|
type: "GET",
|
||||||
method: "erpnext.selling.doctype.sales_order.sales_order.make_purchase_order_for_drop_shipment",
|
method: "erpnext.selling.doctype.sales_order.sales_order.make_purchase_order",
|
||||||
args: {
|
args: {
|
||||||
"source_name": me.frm.doc.name,
|
"source_name": me.frm.doc.name,
|
||||||
"for_supplier": args.supplier
|
"for_supplier": args.supplier,
|
||||||
|
"selected_items": selected_items_list
|
||||||
},
|
},
|
||||||
freeze: true,
|
freeze: true,
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
@@ -576,6 +600,8 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
dialog.get_field("items_for_po").grid.only_sortable()
|
||||||
|
dialog.get_field("items_for_po").refresh()
|
||||||
dialog.show();
|
dialog.show();
|
||||||
},
|
},
|
||||||
hold_sales_order: function(){
|
hold_sales_order: function(){
|
||||||
|
|||||||
@@ -492,13 +492,27 @@ def close_or_unclose_sales_orders(names, status):
|
|||||||
|
|
||||||
frappe.local.message_log = []
|
frappe.local.message_log = []
|
||||||
|
|
||||||
|
def get_requested_item_qty(sales_order):
|
||||||
|
return frappe._dict(frappe.db.sql("""
|
||||||
|
select sales_order_item, sum(stock_qty)
|
||||||
|
from `tabMaterial Request Item`
|
||||||
|
where docstatus = 1
|
||||||
|
and sales_order = %s
|
||||||
|
group by sales_order_item
|
||||||
|
""", sales_order))
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_material_request(source_name, target_doc=None):
|
def make_material_request(source_name, target_doc=None):
|
||||||
|
requested_item_qty = get_requested_item_qty(source_name)
|
||||||
|
|
||||||
def postprocess(source, doc):
|
def postprocess(source, doc):
|
||||||
doc.material_request_type = "Purchase"
|
doc.material_request_type = "Purchase"
|
||||||
|
|
||||||
def update_item(source, target, source_parent):
|
def update_item(source, target, source_parent):
|
||||||
target.project = source_parent.project
|
target.project = source_parent.project
|
||||||
|
target.qty = source.stock_qty - requested_item_qty.get(source.name, 0)
|
||||||
|
target.conversion_factor = 1
|
||||||
|
target.stock_qty = source.stock_qty - requested_item_qty.get(source.name, 0)
|
||||||
|
|
||||||
doc = get_mapped_doc("Sales Order", source_name, {
|
doc = get_mapped_doc("Sales Order", source_name, {
|
||||||
"Sales Order": {
|
"Sales Order": {
|
||||||
@@ -523,7 +537,7 @@ def make_material_request(source_name, target_doc=None):
|
|||||||
"stock_uom": "uom",
|
"stock_uom": "uom",
|
||||||
"stock_qty": "qty"
|
"stock_qty": "qty"
|
||||||
},
|
},
|
||||||
"condition": lambda doc: not frappe.db.exists('Product Bundle', doc.item_code),
|
"condition": lambda doc: not frappe.db.exists('Product Bundle', doc.item_code) and doc.stock_qty > requested_item_qty.get(doc.name, 0),
|
||||||
"postprocess": update_item
|
"postprocess": update_item
|
||||||
}
|
}
|
||||||
}, target_doc, postprocess)
|
}, target_doc, postprocess)
|
||||||
@@ -547,12 +561,6 @@ def make_project(source_name, target_doc=None):
|
|||||||
"base_grand_total" : "estimated_costing",
|
"base_grand_total" : "estimated_costing",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Sales Order Item": {
|
|
||||||
"doctype": "Project Task",
|
|
||||||
"field_map": {
|
|
||||||
"item_code": "title",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}, target_doc, postprocess)
|
}, target_doc, postprocess)
|
||||||
|
|
||||||
return doc
|
return doc
|
||||||
@@ -764,7 +772,10 @@ def get_events(start, end, filters=None):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_purchase_order_for_drop_shipment(source_name, for_supplier=None, target_doc=None):
|
def make_purchase_order(source_name, for_supplier=None, selected_items=[], target_doc=None):
|
||||||
|
if isinstance(selected_items, string_types):
|
||||||
|
selected_items = json.loads(selected_items)
|
||||||
|
|
||||||
def set_missing_values(source, target):
|
def set_missing_values(source, target):
|
||||||
target.supplier = supplier
|
target.supplier = supplier
|
||||||
target.apply_discount_on = ""
|
target.apply_discount_on = ""
|
||||||
@@ -843,7 +854,7 @@ def make_purchase_order_for_drop_shipment(source_name, for_supplier=None, target
|
|||||||
"price_list_rate"
|
"price_list_rate"
|
||||||
],
|
],
|
||||||
"postprocess": update_item,
|
"postprocess": update_item,
|
||||||
"condition": lambda doc: doc.ordered_qty < doc.qty and doc.supplier == supplier
|
"condition": lambda doc: doc.ordered_qty < doc.qty and doc.supplier == supplier and doc.item_code in selected_items
|
||||||
}
|
}
|
||||||
}, target_doc, set_missing_values)
|
}, target_doc, set_missing_values)
|
||||||
if not for_supplier:
|
if not for_supplier:
|
||||||
|
|||||||
@@ -449,7 +449,7 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 1)
|
frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 1)
|
||||||
|
|
||||||
def test_drop_shipping(self):
|
def test_drop_shipping(self):
|
||||||
from erpnext.selling.doctype.sales_order.sales_order import make_purchase_order_for_drop_shipment
|
from erpnext.selling.doctype.sales_order.sales_order import make_purchase_order
|
||||||
from erpnext.buying.doctype.purchase_order.purchase_order import update_status
|
from erpnext.buying.doctype.purchase_order.purchase_order import update_status
|
||||||
|
|
||||||
make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100)
|
make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100)
|
||||||
@@ -495,7 +495,7 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
so = make_sales_order(item_list=so_items, do_not_submit=True)
|
so = make_sales_order(item_list=so_items, do_not_submit=True)
|
||||||
so.submit()
|
so.submit()
|
||||||
|
|
||||||
po = make_purchase_order_for_drop_shipment(so.name, '_Test Supplier')
|
po = make_purchase_order(so.name, '_Test Supplier', selected_items=[so_items[0]['item_code']])
|
||||||
po.submit()
|
po.submit()
|
||||||
|
|
||||||
dn = create_dn_against_so(so.name, delivered_qty=1)
|
dn = create_dn_against_so(so.name, delivered_qty=1)
|
||||||
|
|||||||
@@ -59,6 +59,12 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(this.frm.fields_dict.tc_name) {
|
||||||
|
this.frm.set_query("tc_name", function() {
|
||||||
|
return { filters: { selling: 1 } };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if(!this.frm.fields_dict["items"]) {
|
if(!this.frm.fields_dict["items"]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -145,6 +151,11 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
discount_amount: function(doc, cdt, cdn) {
|
discount_amount: function(doc, cdt, cdn) {
|
||||||
|
|
||||||
|
if(doc.name === cdn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var item = frappe.get_doc(cdt, cdn);
|
var item = frappe.get_doc(cdt, cdn);
|
||||||
item.discount_percentage = 0.0;
|
item.discount_percentage = 0.0;
|
||||||
this.apply_discount_on_item(doc, cdt, cdn, 'discount_amount');
|
this.apply_discount_on_item(doc, cdt, cdn, 'discount_amount');
|
||||||
|
|||||||
@@ -17,6 +17,14 @@ frappe.ui.form.on("Company", {
|
|||||||
filters: {"is_group": 1}
|
filters: {"is_group": 1}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frm.set_query("default_selling_terms", function() {
|
||||||
|
return { filters: { selling: 1 } };
|
||||||
|
});
|
||||||
|
|
||||||
|
frm.set_query("default_buying_terms", function() {
|
||||||
|
return { filters: { buying: 1 } };
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
company_name: function(frm) {
|
company_name: function(frm) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,288 +1,142 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"allow_rename": 1,
|
"allow_rename": 1,
|
||||||
"autoname": "field:title",
|
"autoname": "field:title",
|
||||||
"beta": 0,
|
|
||||||
"creation": "2013-01-10 16:34:24",
|
"creation": "2013-01-10 16:34:24",
|
||||||
"custom": 0,
|
|
||||||
"description": "Standard Terms and Conditions that can be added to Sales and Purchases.\n\nExamples:\n\n1. Validity of the offer.\n1. Payment Terms (In Advance, On Credit, part advance etc).\n1. What is extra (or payable by the Customer).\n1. Safety / usage warning.\n1. Warranty if any.\n1. Returns Policy.\n1. Terms of shipping, if applicable.\n1. Ways of addressing disputes, indemnity, liability, etc.\n1. Address and Contact of your Company.",
|
"description": "Standard Terms and Conditions that can be added to Sales and Purchases.\n\nExamples:\n\n1. Validity of the offer.\n1. Payment Terms (In Advance, On Credit, part advance etc).\n1. What is extra (or payable by the Customer).\n1. Safety / usage warning.\n1. Warranty if any.\n1. Returns Policy.\n1. Terms of shipping, if applicable.\n1. Ways of addressing disputes, indemnity, liability, etc.\n1. Address and Contact of your Company.",
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "Setup",
|
"document_type": "Setup",
|
||||||
"editable_grid": 0,
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"title",
|
||||||
|
"disabled",
|
||||||
|
"applicable_modules_section",
|
||||||
|
"selling",
|
||||||
|
"buying",
|
||||||
|
"hr",
|
||||||
|
"section_break_7",
|
||||||
|
"terms",
|
||||||
|
"terms_and_conditions_help"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "title",
|
"fieldname": "title",
|
||||||
"fieldtype": "Data",
|
"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": "Title",
|
"label": "Title",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "title",
|
"oldfieldname": "title",
|
||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 1
|
"unique": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"default": "0",
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "disabled",
|
"fieldname": "disabled",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
"label": "Disabled"
|
||||||
"label": "Disabled",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_in_quick_entry": 1,
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "terms",
|
"fieldname": "terms",
|
||||||
"fieldtype": "Text Editor",
|
"fieldtype": "Text Editor",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Terms and Conditions",
|
"label": "Terms and Conditions",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "terms",
|
"oldfieldname": "terms",
|
||||||
"oldfieldtype": "Text Editor",
|
"oldfieldtype": "Text Editor"
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "terms_and_conditions_help",
|
"fieldname": "terms_and_conditions_help",
|
||||||
"fieldtype": "HTML",
|
"fieldtype": "HTML",
|
||||||
"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": "Terms and Conditions Help",
|
"label": "Terms and Conditions Help",
|
||||||
"length": 0,
|
"options": "<h4>Standard Terms and Conditions Example</h4>\n\n<pre>Delivery Terms for Order number {{ name }}\n\n-Order Date : {{ transaction_date }} \n-Expected Delivery Date : {{ delivery_date }}\n</pre>\n\n<h4>How to get fieldnames</h4>\n\n<p>The fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)</p>\n\n<h4>Templating</h4>\n\n<p>Templates are compiled using the Jinja Templating Langauge. To learn more about Jinja, <a class=\"strong\" href=\"http://jinja.pocoo.org/docs/dev/templates/\">read this documentation.</a></p>"
|
||||||
"no_copy": 0,
|
},
|
||||||
"options": "<h4>Standard Terms and Conditions Example</h4>\n\n<pre>Delivery Terms for Order number {{ name }}\n\n-Order Date : {{ transaction_date }} \n-Expected Delivery Date : {{ delivery_date }}\n</pre>\n\n<h4>How to get fieldnames</h4>\n\n<p>The fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)</p>\n\n<h4>Templating</h4>\n\n<p>Templates are compiled using the Jinja Templating Langauge. To learn more about Jinja, <a class=\"strong\" href=\"http://jinja.pocoo.org/docs/dev/templates/\">read this documentation.</a></p>",
|
{
|
||||||
"permlevel": 0,
|
"fieldname": "applicable_modules_section",
|
||||||
"precision": "",
|
"fieldtype": "Section Break",
|
||||||
"print_hide": 0,
|
"label": "Applicable Modules"
|
||||||
"print_hide_if_no_value": 0,
|
},
|
||||||
"read_only": 0,
|
{
|
||||||
"remember_last_selected_value": 0,
|
"default": "1",
|
||||||
"report_hide": 0,
|
"fieldname": "selling",
|
||||||
"reqd": 0,
|
"fieldtype": "Check",
|
||||||
"search_index": 0,
|
"label": "Selling"
|
||||||
"set_only_once": 0,
|
},
|
||||||
"translatable": 0,
|
{
|
||||||
"unique": 0
|
"default": "1",
|
||||||
|
"fieldname": "buying",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Buying"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "1",
|
||||||
|
"fieldname": "hr",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "HR"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_7",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"icon": "icon-legal",
|
"icon": "icon-legal",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"image_view": 0,
|
"modified": "2019-07-04 13:31:30.393425",
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2018-08-29 06:36:33.131473",
|
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Setup",
|
"module": "Setup",
|
||||||
"name": "Terms and Conditions",
|
"name": "Terms and Conditions",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 1,
|
"import": 1,
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Sales Master Manager",
|
"role": "Sales Master Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 0,
|
|
||||||
"delete": 0,
|
|
||||||
"email": 0,
|
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 0,
|
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 0,
|
"role": "Sales User"
|
||||||
"role": "Sales User",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 0,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 0,
|
|
||||||
"delete": 0,
|
|
||||||
"email": 0,
|
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 0,
|
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 0,
|
"role": "Purchase User"
|
||||||
"role": "Purchase User",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 0,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "System Manager",
|
"role": "System Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Accounts User",
|
"role": "Accounts User",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 0,
|
|
||||||
"delete": 0,
|
|
||||||
"email": 0,
|
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 0,
|
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 0,
|
"role": "Stock User"
|
||||||
"role": "Stock User",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 0,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 1,
|
"show_name_in_global_search": 1,
|
||||||
"sort_order": "ASC",
|
"sort_field": "modified",
|
||||||
"track_changes": 0,
|
"sort_order": "ASC"
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
||||||
@@ -3,9 +3,11 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe import _, throw
|
||||||
import json
|
import json
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils.jinja import validate_template
|
from frappe.utils.jinja import validate_template
|
||||||
|
from frappe.utils import cint
|
||||||
|
|
||||||
from six import string_types
|
from six import string_types
|
||||||
|
|
||||||
@@ -13,6 +15,8 @@ class TermsandConditions(Document):
|
|||||||
def validate(self):
|
def validate(self):
|
||||||
if self.terms:
|
if self.terms:
|
||||||
validate_template(self.terms)
|
validate_template(self.terms)
|
||||||
|
if not cint(self.buying) and not cint(self.selling) and not cint(self.hr) and not cint(self.disabled):
|
||||||
|
throw(_("At least one of the Applicable Modules should be selected"))
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_terms_and_conditions(template_name, doc):
|
def get_terms_and_conditions(template_name, doc):
|
||||||
|
|||||||
@@ -251,11 +251,13 @@ def _get_cart_quotation(party=None):
|
|||||||
if quotation:
|
if quotation:
|
||||||
qdoc = frappe.get_doc("Quotation", quotation[0].name)
|
qdoc = frappe.get_doc("Quotation", quotation[0].name)
|
||||||
else:
|
else:
|
||||||
|
[company, price_list] = frappe.db.get_value("Shopping Cart Settings", None, ["company", "price_list"])
|
||||||
qdoc = frappe.get_doc({
|
qdoc = frappe.get_doc({
|
||||||
"doctype": "Quotation",
|
"doctype": "Quotation",
|
||||||
"naming_series": get_shopping_cart_settings().quotation_series or "QTN-CART-",
|
"naming_series": get_shopping_cart_settings().quotation_series or "QTN-CART-",
|
||||||
"quotation_to": party.doctype,
|
"quotation_to": party.doctype,
|
||||||
"company": frappe.db.get_value("Shopping Cart Settings", None, "company"),
|
"company": company,
|
||||||
|
"selling_price_list": price_list,
|
||||||
"order_type": "Shopping Cart",
|
"order_type": "Shopping Cart",
|
||||||
"status": "Draft",
|
"status": "Draft",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ def boot_session(bootinfo):
|
|||||||
FROM `tabCompany`
|
FROM `tabCompany`
|
||||||
LIMIT 1""") and 'Yes' or 'No'
|
LIMIT 1""") and 'Yes' or 'No'
|
||||||
|
|
||||||
bootinfo.docs += frappe.db.sql("""select name, default_currency, cost_center, default_terms,
|
bootinfo.docs += frappe.db.sql("""select name, default_currency, cost_center, default_selling_terms, default_buying_terms,
|
||||||
default_letter_head, default_bank_account, enable_perpetual_inventory, country from `tabCompany`""",
|
default_letter_head, default_bank_account, enable_perpetual_inventory, country from `tabCompany`""",
|
||||||
as_dict=1, update={"doctype":":Company"})
|
as_dict=1, update={"doctype":":Company"})
|
||||||
|
|
||||||
|
|||||||
@@ -77,8 +77,34 @@ frappe.ui.form.on("Delivery Note", {
|
|||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
print_without_amount: function(frm) {
|
print_without_amount: function(frm) {
|
||||||
erpnext.stock.delivery_note.set_print_hide(frm.doc);
|
erpnext.stock.delivery_note.set_print_hide(frm.doc);
|
||||||
|
},
|
||||||
|
|
||||||
|
refresh: function(frm) {
|
||||||
|
if (frm.doc.docstatus === 1 && frm.doc.is_return === 1 && frm.doc.per_billed !== 100) {
|
||||||
|
frm.add_custom_button(__('Credit Note'), function() {
|
||||||
|
frappe.confirm(__("Are you sure you want to make credit note?"),
|
||||||
|
function() {
|
||||||
|
frm.trigger("make_credit_note");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}, __('Create'));
|
||||||
|
|
||||||
|
frm.page.set_inner_btn_group_as_primary(__('Create'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
make_credit_note: function(frm) {
|
||||||
|
frm.call({
|
||||||
|
method: "make_return_invoice",
|
||||||
|
doc: frm.doc,
|
||||||
|
freeze: true,
|
||||||
|
callback: function() {
|
||||||
|
frm.reload_doc();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -333,7 +333,10 @@ class DeliveryNote(SellingController):
|
|||||||
return_invoice.is_return = True
|
return_invoice.is_return = True
|
||||||
return_invoice.save()
|
return_invoice.save()
|
||||||
return_invoice.submit()
|
return_invoice.submit()
|
||||||
frappe.msgprint(_("Credit Note {0} has been created automatically").format(return_invoice.name))
|
|
||||||
|
credit_note_link = frappe.utils.get_link_to_form('Sales Invoice', return_invoice.name)
|
||||||
|
|
||||||
|
frappe.msgprint(_("Credit Note {0} has been created automatically").format(credit_note_link))
|
||||||
except:
|
except:
|
||||||
frappe.throw(_("Could not create Credit Note automatically, please uncheck 'Issue Credit Note' and submit again"))
|
frappe.throw(_("Could not create Credit Note automatically, please uncheck 'Issue Credit Note' and submit again"))
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,6 @@
|
|||||||
"is_customer_provided_item",
|
"is_customer_provided_item",
|
||||||
"customer",
|
"customer",
|
||||||
"supplier_details",
|
"supplier_details",
|
||||||
"manufacturers",
|
|
||||||
"delivered_by_supplier",
|
"delivered_by_supplier",
|
||||||
"column_break2",
|
"column_break2",
|
||||||
"supplier_items",
|
"supplier_items",
|
||||||
@@ -1022,12 +1021,6 @@
|
|||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Synced With Hub",
|
"label": "Synced With Hub",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "manufacturers",
|
|
||||||
"fieldtype": "Table",
|
|
||||||
"label": "Manufacturers",
|
|
||||||
"options": "Item Manufacturer"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 1,
|
"has_web_view": 1,
|
||||||
@@ -1035,7 +1028,7 @@
|
|||||||
"idx": 2,
|
"idx": 2,
|
||||||
"image_field": "image",
|
"image_field": "image",
|
||||||
"max_attachments": 1,
|
"max_attachments": 1,
|
||||||
"modified": "2019-06-02 04:45:59.911507",
|
"modified": "2019-07-05 12:18:13.977931",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Item",
|
"name": "Item",
|
||||||
@@ -1097,4 +1090,4 @@
|
|||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"title_field": "item_name",
|
"title_field": "item_name",
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ class Item(WebsiteGenerator):
|
|||||||
self.validate_item_defaults()
|
self.validate_item_defaults()
|
||||||
self.validate_customer_provided_part()
|
self.validate_customer_provided_part()
|
||||||
self.update_defaults_from_item_group()
|
self.update_defaults_from_item_group()
|
||||||
|
self.validate_auto_reorder_enabled_in_stock_settings()
|
||||||
self.cant_change()
|
self.cant_change()
|
||||||
|
|
||||||
if not self.get("__islocal"):
|
if not self.get("__islocal"):
|
||||||
@@ -859,6 +860,12 @@ class Item(WebsiteGenerator):
|
|||||||
filters={"production_item": self.name, "docstatus": 1}):
|
filters={"production_item": self.name, "docstatus": 1}):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def validate_auto_reorder_enabled_in_stock_settings(self):
|
||||||
|
if self.reorder_levels:
|
||||||
|
enabled = frappe.db.get_single_value('Stock Settings', 'auto_indent')
|
||||||
|
if not enabled:
|
||||||
|
frappe.msgprint(msg=_("You have to enable auto re-order in Stock Settings to maintain re-order levels."), title=_("Enable Auto Re-Order"), indicator="orange")
|
||||||
|
|
||||||
def get_timeline_data(doctype, name):
|
def get_timeline_data(doctype, name):
|
||||||
'''returns timeline data based on stock ledger entry'''
|
'''returns timeline data based on stock ledger entry'''
|
||||||
out = {}
|
out = {}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ def get_data():
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
'label': _('Manufacture'),
|
'label': _('Manufacture'),
|
||||||
'items': ['Work Order', 'Item Manufacturer']
|
'items': ['Production Plan', 'Work Order', 'Item Manufacturer']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,13 +31,16 @@ class ItemPrice(Document):
|
|||||||
frappe.throw(_("Valid From Date must be lesser than Valid Upto Date."))
|
frappe.throw(_("Valid From Date must be lesser than Valid Upto Date."))
|
||||||
|
|
||||||
def update_price_list_details(self):
|
def update_price_list_details(self):
|
||||||
self.buying, self.selling, self.currency = \
|
if self.price_list:
|
||||||
frappe.db.get_value("Price List",
|
self.buying, self.selling, self.currency = \
|
||||||
{"name": self.price_list, "enabled": 1},
|
frappe.db.get_value("Price List",
|
||||||
["buying", "selling", "currency"])
|
{"name": self.price_list, "enabled": 1},
|
||||||
|
["buying", "selling", "currency"])
|
||||||
|
|
||||||
def update_item_details(self):
|
def update_item_details(self):
|
||||||
self.item_name, self.item_description = frappe.db.get_value("Item",self.item_code,["item_name", "description"])
|
if self.item_code:
|
||||||
|
self.item_name, self.item_description = frappe.db.get_value("Item",
|
||||||
|
self.item_code,["item_name", "description"])
|
||||||
|
|
||||||
def check_duplicates(self):
|
def check_duplicates(self):
|
||||||
conditions = "where item_code=%(item_code)s and price_list=%(price_list)s and name != %(name)s"
|
conditions = "where item_code=%(item_code)s and price_list=%(price_list)s and name != %(name)s"
|
||||||
|
|||||||
@@ -370,19 +370,20 @@ def make_purchase_order_based_on_supplier(source_name, target_doc=None):
|
|||||||
def get_material_requests_based_on_supplier(supplier):
|
def get_material_requests_based_on_supplier(supplier):
|
||||||
supplier_items = [d.parent for d in frappe.db.get_all("Item Default",
|
supplier_items = [d.parent for d in frappe.db.get_all("Item Default",
|
||||||
{"default_supplier": supplier}, 'parent')]
|
{"default_supplier": supplier}, 'parent')]
|
||||||
if supplier_items:
|
if not supplier_items:
|
||||||
material_requests = frappe.db.sql_list("""select distinct mr.name
|
frappe.throw(_("{0} is not the default supplier for any items.".format(supplier)))
|
||||||
from `tabMaterial Request` mr, `tabMaterial Request Item` mr_item
|
|
||||||
where mr.name = mr_item.parent
|
material_requests = frappe.db.sql_list("""select distinct mr.name
|
||||||
and mr_item.item_code in (%s)
|
from `tabMaterial Request` mr, `tabMaterial Request Item` mr_item
|
||||||
and mr.material_request_type = 'Purchase'
|
where mr.name = mr_item.parent
|
||||||
and mr.per_ordered < 99.99
|
and mr_item.item_code in (%s)
|
||||||
and mr.docstatus = 1
|
and mr.material_request_type = 'Purchase'
|
||||||
and mr.status != 'Stopped'
|
and mr.per_ordered < 99.99
|
||||||
order by mr_item.item_code ASC""" % ', '.join(['%s']*len(supplier_items)),
|
and mr.docstatus = 1
|
||||||
tuple(supplier_items))
|
and mr.status != 'Stopped'
|
||||||
else:
|
order by mr_item.item_code ASC""" % ', '.join(['%s']*len(supplier_items)),
|
||||||
material_requests = []
|
tuple(supplier_items))
|
||||||
|
|
||||||
return material_requests, supplier_items
|
return material_requests, supplier_items
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
|
|||||||
@@ -38,6 +38,29 @@ frappe.ui.form.on("Purchase Receipt", {
|
|||||||
if(frm.doc.company) {
|
if(frm.doc.company) {
|
||||||
frm.trigger("toggle_display_account_head");
|
frm.trigger("toggle_display_account_head");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (frm.doc.docstatus === 1 && frm.doc.is_return === 1 && frm.doc.per_billed !== 100) {
|
||||||
|
frm.add_custom_button(__('Debit Note'), function() {
|
||||||
|
frappe.confirm(__("Are you sure you want to make debit note?"),
|
||||||
|
function() {
|
||||||
|
frm.trigger("make_debit_note");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}, __('Create'));
|
||||||
|
|
||||||
|
frm.page.set_inner_btn_group_as_primary(__('Create'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
make_debit_note: function(frm) {
|
||||||
|
frm.call({
|
||||||
|
method: "make_return_invoice",
|
||||||
|
doc: frm.doc,
|
||||||
|
freeze: true,
|
||||||
|
callback: function() {
|
||||||
|
frm.reload_doc();
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
company: function(frm) {
|
company: function(frm) {
|
||||||
|
|||||||
@@ -387,6 +387,16 @@ class PurchaseReceipt(BuyingController):
|
|||||||
|
|
||||||
self.load_from_db()
|
self.load_from_db()
|
||||||
|
|
||||||
|
def make_return_invoice(self):
|
||||||
|
return_invoice = make_purchase_invoice(self.name)
|
||||||
|
return_invoice.is_return = True
|
||||||
|
return_invoice.save()
|
||||||
|
return_invoice.submit()
|
||||||
|
|
||||||
|
debit_note_link = frappe.utils.get_link_to_form('Purchase Invoice', return_invoice.name)
|
||||||
|
|
||||||
|
frappe.msgprint(_("Debit Note {0} has been created automatically").format(debit_note_link))
|
||||||
|
|
||||||
def update_billed_amount_based_on_po(po_detail, update_modified=True):
|
def update_billed_amount_based_on_po(po_detail, update_modified=True):
|
||||||
# Billed against Sales Order directly
|
# Billed against Sales Order directly
|
||||||
billed_against_po = frappe.db.sql("""select sum(amount) from `tabPurchase Invoice Item`
|
billed_against_po = frappe.db.sql("""select sum(amount) from `tabPurchase Invoice Item`
|
||||||
|
|||||||
@@ -324,8 +324,10 @@ class StockEntry(StockController):
|
|||||||
completed_qty = d.completed_qty + (allowance_percentage/100 * d.completed_qty)
|
completed_qty = d.completed_qty + (allowance_percentage/100 * d.completed_qty)
|
||||||
if total_completed_qty > flt(completed_qty):
|
if total_completed_qty > flt(completed_qty):
|
||||||
job_card = frappe.db.get_value('Job Card', {'operation_id': d.name}, 'name')
|
job_card = frappe.db.get_value('Job Card', {'operation_id': d.name}, 'name')
|
||||||
frappe.throw(_("Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order # {3}. Please update operation status via Job Card # {4}")
|
work_order_link = frappe.utils.get_link_to_form('Work Order', self.work_order)
|
||||||
.format(d.idx, d.operation, total_completed_qty, self.work_order, job_card), OperationsNotCompleteError)
|
job_card_link = frappe.utils.get_link_to_form('Job Card', job_card)
|
||||||
|
frappe.throw(_("Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order {3}. Please update operation status via Job Card {4}.")
|
||||||
|
.format(d.idx, frappe.bold(d.operation), frappe.bold(total_completed_qty), work_order_link, job_card_link), OperationsNotCompleteError)
|
||||||
|
|
||||||
def check_duplicate_entry_for_work_order(self):
|
def check_duplicate_entry_for_work_order(self):
|
||||||
other_ste = [t[0] for t in frappe.db.get_values("Stock Entry", {
|
other_ste = [t[0] for t in frappe.db.get_values("Stock Entry", {
|
||||||
|
|||||||
@@ -129,8 +129,15 @@ frappe.ui.form.on("Issue", {
|
|||||||
function set_time_to_resolve_and_response(frm) {
|
function set_time_to_resolve_and_response(frm) {
|
||||||
frm.dashboard.clear_headline();
|
frm.dashboard.clear_headline();
|
||||||
|
|
||||||
var time_to_respond = get_time_left(frm.doc.response_by, frm.doc.agreement_fulfilled);
|
var time_to_respond = get_status(frm.doc.response_by_variance);
|
||||||
var time_to_resolve = get_time_left(frm.doc.resolution_by, frm.doc.agreement_fulfilled);
|
if (!frm.doc.first_responded_on && frm.doc.agreement_fulfilled === "Ongoing") {
|
||||||
|
time_to_respond = get_time_left(frm.doc.response_by, frm.doc.agreement_fulfilled);
|
||||||
|
}
|
||||||
|
|
||||||
|
var time_to_resolve = get_status(frm.doc.resolution_by_variance);
|
||||||
|
if (!frm.doc.resolution_date && frm.doc.agreement_fulfilled === "Ongoing") {
|
||||||
|
time_to_resolve = get_time_left(frm.doc.response_by, frm.doc.agreement_fulfilled);
|
||||||
|
}
|
||||||
|
|
||||||
frm.dashboard.set_headline_alert(
|
frm.dashboard.set_headline_alert(
|
||||||
'<div class="row">' +
|
'<div class="row">' +
|
||||||
@@ -146,7 +153,15 @@ function set_time_to_resolve_and_response(frm) {
|
|||||||
|
|
||||||
function get_time_left(timestamp, agreement_fulfilled) {
|
function get_time_left(timestamp, agreement_fulfilled) {
|
||||||
const diff = moment(timestamp).diff(moment());
|
const diff = moment(timestamp).diff(moment());
|
||||||
const diff_display = diff >= 44500 ? moment.duration(diff).humanize() : moment(0, 'seconds').format('HH:mm');
|
const diff_display = diff >= 44500 ? moment.duration(diff).humanize() : "Failed";
|
||||||
let indicator = (diff_display == '00:00' && agreement_fulfilled != "Fulfilled") ? "red" : "green";
|
let indicator = (diff_display == 'Failed' && agreement_fulfilled != "Fulfilled") ? "red" : "green";
|
||||||
return {"diff_display": diff_display, "indicator": indicator};
|
return {"diff_display": diff_display, "indicator": indicator};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function get_status(variance) {
|
||||||
|
if (variance > 0) {
|
||||||
|
return {"diff_display": "Fulfilled", "indicator": "green"};
|
||||||
|
} else {
|
||||||
|
return {"diff_display": "Failed", "indicator": "red"};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -113,6 +113,7 @@
|
|||||||
"search_index": 1
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"default": "Medium",
|
||||||
"fieldname": "priority",
|
"fieldname": "priority",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
@@ -143,7 +144,6 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"collapsible": 1,
|
"collapsible": 1,
|
||||||
"depends_on": "eval: doc.service_level_agreement",
|
|
||||||
"fieldname": "service_level_section",
|
"fieldname": "service_level_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Service Level"
|
"label": "Service Level"
|
||||||
@@ -314,6 +314,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "Ongoing",
|
"default": "Ongoing",
|
||||||
|
"depends_on": "eval: doc.service_level_agreement",
|
||||||
"fieldname": "agreement_fulfilled",
|
"fieldname": "agreement_fulfilled",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Service Level Agreement Fulfilled",
|
"label": "Service Level Agreement Fulfilled",
|
||||||
@@ -321,6 +322,7 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval: doc.service_level_agreement",
|
||||||
"description": "in hours",
|
"description": "in hours",
|
||||||
"fieldname": "response_by_variance",
|
"fieldname": "response_by_variance",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
@@ -328,6 +330,7 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval: doc.service_level_agreement",
|
||||||
"description": "in hours",
|
"description": "in hours",
|
||||||
"fieldname": "resolution_by_variance",
|
"fieldname": "resolution_by_variance",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
@@ -337,7 +340,7 @@
|
|||||||
],
|
],
|
||||||
"icon": "fa fa-ticket",
|
"icon": "fa fa-ticket",
|
||||||
"idx": 7,
|
"idx": 7,
|
||||||
"modified": "2019-06-27 15:19:00.771333",
|
"modified": "2019-06-30 13:19:38.215525",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Support",
|
"module": "Support",
|
||||||
"name": "Issue",
|
"name": "Issue",
|
||||||
|
|||||||
@@ -92,7 +92,6 @@ class Issue(Document):
|
|||||||
self.resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_datetime()), 2)
|
self.resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_datetime()), 2)
|
||||||
|
|
||||||
self.agreement_fulfilled = "Fulfilled" if self.response_by_variance > 0 and self.resolution_by_variance > 0 else "Failed"
|
self.agreement_fulfilled = "Fulfilled" if self.response_by_variance > 0 and self.resolution_by_variance > 0 else "Failed"
|
||||||
self.save(ignore_permissions=True)
|
|
||||||
|
|
||||||
def create_communication(self):
|
def create_communication(self):
|
||||||
communication = frappe.new_doc("Communication")
|
communication = frappe.new_doc("Communication")
|
||||||
@@ -118,6 +117,17 @@ class Issue(Document):
|
|||||||
|
|
||||||
replicated_issue = deepcopy(self)
|
replicated_issue = deepcopy(self)
|
||||||
replicated_issue.subject = subject
|
replicated_issue.subject = subject
|
||||||
|
replicated_issue.creation = now_datetime()
|
||||||
|
|
||||||
|
# Reset SLA
|
||||||
|
if replicated_issue.service_level_agreement:
|
||||||
|
replicated_issue.service_level_agreement = None
|
||||||
|
replicated_issue.agreement_fulfilled = "Ongoing"
|
||||||
|
replicated_issue.response_by = None
|
||||||
|
replicated_issue.response_by_variance = None
|
||||||
|
replicated_issue.resolution_by = None
|
||||||
|
replicated_issue.resolution_by_variance = None
|
||||||
|
|
||||||
frappe.get_doc(replicated_issue).insert()
|
frappe.get_doc(replicated_issue).insert()
|
||||||
|
|
||||||
# Replicate linked Communications
|
# Replicate linked Communications
|
||||||
@@ -136,7 +146,8 @@ class Issue(Document):
|
|||||||
return replicated_issue.name
|
return replicated_issue.name
|
||||||
|
|
||||||
def before_insert(self):
|
def before_insert(self):
|
||||||
self.set_response_and_resolution_time()
|
if frappe.db.get_single_value("Support Settings", "track_service_level_agreement"):
|
||||||
|
self.set_response_and_resolution_time()
|
||||||
|
|
||||||
def set_response_and_resolution_time(self, priority=None, service_level_agreement=None):
|
def set_response_and_resolution_time(self, priority=None, service_level_agreement=None):
|
||||||
service_level_agreement = get_active_service_level_agreement_for(priority=priority,
|
service_level_agreement = get_active_service_level_agreement_for(priority=priority,
|
||||||
@@ -171,13 +182,16 @@ class Issue(Document):
|
|||||||
self.resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_datetime()))
|
self.resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_datetime()))
|
||||||
|
|
||||||
def change_service_level_agreement_and_priority(self):
|
def change_service_level_agreement_and_priority(self):
|
||||||
if not self.priority == frappe.db.get_value("Issue", self.name, "priority"):
|
if self.service_level_agreement and frappe.db.exists("Issue", self.name) and \
|
||||||
self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement)
|
frappe.db.get_single_value("Support Settings", "track_service_level_agreement"):
|
||||||
frappe.msgprint("Priority has been updated.")
|
|
||||||
|
|
||||||
if not self.service_level_agreement == frappe.db.get_value("Issue", self.name, "service_level_agreement"):
|
if not self.priority == frappe.db.get_value("Issue", self.name, "priority"):
|
||||||
self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement)
|
self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement)
|
||||||
frappe.msgprint("Service Level Agreement has been updated.")
|
frappe.msgprint(_("Priority has been changed to {0}.").format(self.priority))
|
||||||
|
|
||||||
|
if not self.service_level_agreement == frappe.db.get_value("Issue", self.name, "service_level_agreement"):
|
||||||
|
self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement)
|
||||||
|
frappe.msgprint(_("Service Level Agreement has been changed to {0}.").format(self.service_level_agreement))
|
||||||
|
|
||||||
def get_expected_time_for(parameter, service_level, start_date_time):
|
def get_expected_time_for(parameter, service_level, start_date_time):
|
||||||
current_date_time = start_date_time
|
current_date_time = start_date_time
|
||||||
@@ -258,15 +272,15 @@ def set_service_level_agreement_variance(issue=None):
|
|||||||
|
|
||||||
if not doc.first_responded_on: # first_responded_on set when first reply is sent to customer
|
if not doc.first_responded_on: # first_responded_on set when first reply is sent to customer
|
||||||
variance = round(time_diff_in_hours(doc.response_by, current_time), 2)
|
variance = round(time_diff_in_hours(doc.response_by, current_time), 2)
|
||||||
frappe.db.set_value("Issue", doc.name, "response_by_variance", variance)
|
frappe.db.set_value(dt="Issue", dn=doc.name, field="response_by_variance", val=variance, update_modified=False)
|
||||||
if variance < 0:
|
if variance < 0:
|
||||||
frappe.db.set_value("Issue", doc.name, "agreement_fulfilled", "Failed")
|
frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_fulfilled", val="Failed", update_modified=False)
|
||||||
|
|
||||||
if not doc.resolution_date: # resolution_date set when issue has been closed
|
if not doc.resolution_date: # resolution_date set when issue has been closed
|
||||||
variance = round(time_diff_in_hours(doc.resolution_by, current_time), 2)
|
variance = round(time_diff_in_hours(doc.resolution_by, current_time), 2)
|
||||||
frappe.db.set_value("Issue", doc.name, "resolution_by_variance", variance)
|
frappe.db.set_value(dt="Issue", dn=doc.name, field="resolution_by_variance", val=variance, update_modified=False)
|
||||||
if variance < 0:
|
if variance < 0:
|
||||||
frappe.db.set_value("Issue", doc.name, "agreement_fulfilled", "Failed")
|
frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_fulfilled", val="Failed", update_modified=False)
|
||||||
|
|
||||||
def get_list_context(context=None):
|
def get_list_context(context=None):
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from datetime import timedelta
|
|||||||
|
|
||||||
class TestIssue(unittest.TestCase):
|
class TestIssue(unittest.TestCase):
|
||||||
def test_response_time_and_resolution_time_based_on_different_sla(self):
|
def test_response_time_and_resolution_time_based_on_different_sla(self):
|
||||||
|
frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1)
|
||||||
create_service_level_agreements_for_issues()
|
create_service_level_agreements_for_issues()
|
||||||
|
|
||||||
creation = datetime.datetime(2019, 3, 4, 12, 0)
|
creation = datetime.datetime(2019, 3, 4, 12, 0)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
|
"enable",
|
||||||
"service_level",
|
"service_level",
|
||||||
"default_service_level_agreement",
|
"default_service_level_agreement",
|
||||||
"holiday_list",
|
"holiday_list",
|
||||||
@@ -149,9 +150,15 @@
|
|||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Entity Type",
|
"label": "Entity Type",
|
||||||
"options": "\nCustomer\nCustomer Group\nTerritory"
|
"options": "\nCustomer\nCustomer Group\nTerritory"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "1",
|
||||||
|
"fieldname": "enable",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Enable"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2019-06-20 18:04:14.293378",
|
"modified": "2019-07-09 17:22:16.402939",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Support",
|
"module": "Support",
|
||||||
"name": "Service Level Agreement",
|
"name": "Service Level Agreement",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user