Merge branch 'develop' into patch-7
This commit is contained in:
commit
1b8d36634f
@ -214,6 +214,7 @@ class Account(NestedSet):
|
|||||||
if parent_value_changed:
|
if parent_value_changed:
|
||||||
doc.save()
|
doc.save()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def convert_group_to_ledger(self):
|
def convert_group_to_ledger(self):
|
||||||
if self.check_if_child_exists():
|
if self.check_if_child_exists():
|
||||||
throw(_("Account with child nodes cannot be converted to ledger"))
|
throw(_("Account with child nodes cannot be converted to ledger"))
|
||||||
@ -224,6 +225,7 @@ class Account(NestedSet):
|
|||||||
self.save()
|
self.save()
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def convert_ledger_to_group(self):
|
def convert_ledger_to_group(self):
|
||||||
if self.check_gle_exists():
|
if self.check_gle_exists():
|
||||||
throw(_("Account with existing transaction can not be converted to group."))
|
throw(_("Account with existing transaction can not be converted to group."))
|
||||||
|
|||||||
@ -39,6 +39,7 @@ class AccountingPeriod(Document):
|
|||||||
frappe.throw(_("Accounting Period overlaps with {0}")
|
frappe.throw(_("Accounting Period overlaps with {0}")
|
||||||
.format(existing_accounting_period[0].get("name")), OverlapError)
|
.format(existing_accounting_period[0].get("name")), OverlapError)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_doctypes_for_closing(self):
|
def get_doctypes_for_closing(self):
|
||||||
docs_for_closing = []
|
docs_for_closing = []
|
||||||
doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", \
|
doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", \
|
||||||
|
|||||||
@ -12,6 +12,7 @@ form_grid_templates = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class BankClearance(Document):
|
class BankClearance(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def get_payment_entries(self):
|
def get_payment_entries(self):
|
||||||
if not (self.from_date and self.to_date):
|
if not (self.from_date and self.to_date):
|
||||||
frappe.throw(_("From Date and To Date are Mandatory"))
|
frappe.throw(_("From Date and To Date are Mandatory"))
|
||||||
@ -108,6 +109,7 @@ class BankClearance(Document):
|
|||||||
row.update(d)
|
row.update(d)
|
||||||
self.total_amount += flt(amount)
|
self.total_amount += flt(amount)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def update_clearance_date(self):
|
def update_clearance_date(self):
|
||||||
clearance_date_updated = False
|
clearance_date_updated = False
|
||||||
for d in self.get('payment_entries'):
|
for d in self.get('payment_entries'):
|
||||||
|
|||||||
@ -532,43 +532,4 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
</table>
|
</table>
|
||||||
`);
|
`);
|
||||||
},
|
},
|
||||||
|
|
||||||
show_missing_link_values(frm, missing_link_values) {
|
|
||||||
let can_be_created_automatically = missing_link_values.every(
|
|
||||||
(d) => d.has_one_mandatory_field
|
|
||||||
);
|
|
||||||
|
|
||||||
let html = missing_link_values
|
|
||||||
.map((d) => {
|
|
||||||
let doctype = d.doctype;
|
|
||||||
let values = d.missing_values;
|
|
||||||
return `
|
|
||||||
<h5>${doctype}</h5>
|
|
||||||
<ul>${values.map((v) => `<li>${v}</li>`).join("")}</ul>
|
|
||||||
`;
|
|
||||||
})
|
|
||||||
.join("");
|
|
||||||
|
|
||||||
if (can_be_created_automatically) {
|
|
||||||
// prettier-ignore
|
|
||||||
let message = __('There are some linked records which needs to be created before we can import your file. Do you want to create the following missing records automatically?');
|
|
||||||
frappe.confirm(message + html, () => {
|
|
||||||
frm.call("create_missing_link_values", {
|
|
||||||
missing_link_values,
|
|
||||||
}).then((r) => {
|
|
||||||
let records = r.message;
|
|
||||||
frappe.msgprint(__(
|
|
||||||
"Created {0} records successfully.", [
|
|
||||||
records.length,
|
|
||||||
]
|
|
||||||
));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
frappe.msgprint(
|
|
||||||
// prettier-ignore
|
|
||||||
__('The following records needs to be created before we can import your file.') + html
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -57,6 +57,7 @@ class CForm(Document):
|
|||||||
total = sum([flt(d.grand_total) for d in self.get('invoices')])
|
total = sum([flt(d.grand_total) for d in self.get('invoices')])
|
||||||
frappe.db.set(self, 'total_invoiced_amount', total)
|
frappe.db.set(self, 'total_invoiced_amount', total)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_invoice_details(self, invoice_no):
|
def get_invoice_details(self, invoice_no):
|
||||||
""" Pull details from invoices for referrence """
|
""" Pull details from invoices for referrence """
|
||||||
if invoice_no:
|
if invoice_no:
|
||||||
|
|||||||
@ -50,6 +50,7 @@ class CostCenter(NestedSet):
|
|||||||
frappe.throw(_("{0} is not a group node. Please select a group node as parent cost center").format(
|
frappe.throw(_("{0} is not a group node. Please select a group node as parent cost center").format(
|
||||||
frappe.bold(self.parent_cost_center)))
|
frappe.bold(self.parent_cost_center)))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def convert_group_to_ledger(self):
|
def convert_group_to_ledger(self):
|
||||||
if self.check_if_child_exists():
|
if self.check_if_child_exists():
|
||||||
frappe.throw(_("Cannot convert Cost Center to ledger as it has child nodes"))
|
frappe.throw(_("Cannot convert Cost Center to ledger as it has child nodes"))
|
||||||
@ -60,6 +61,7 @@ class CostCenter(NestedSet):
|
|||||||
self.save()
|
self.save()
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def convert_ledger_to_group(self):
|
def convert_ledger_to_group(self):
|
||||||
if cint(self.enable_distributed_cost_center):
|
if cint(self.enable_distributed_cost_center):
|
||||||
frappe.throw(_("Cost Center with enabled distributed cost center can not be converted to group"))
|
frappe.throw(_("Cost Center with enabled distributed cost center can not be converted to group"))
|
||||||
|
|||||||
@ -27,6 +27,7 @@ class ExchangeRateRevaluation(Document):
|
|||||||
if not (self.company and self.posting_date):
|
if not (self.company and self.posting_date):
|
||||||
frappe.throw(_("Please select Company and Posting Date to getting entries"))
|
frappe.throw(_("Please select Company and Posting Date to getting entries"))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_accounts_data(self, account=None):
|
def get_accounts_data(self, account=None):
|
||||||
accounts = []
|
accounts = []
|
||||||
self.validate_mandatory()
|
self.validate_mandatory()
|
||||||
@ -95,6 +96,7 @@ class ExchangeRateRevaluation(Document):
|
|||||||
message = _("No outstanding invoices found")
|
message = _("No outstanding invoices found")
|
||||||
frappe.msgprint(message)
|
frappe.msgprint(message)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def make_jv_entry(self):
|
def make_jv_entry(self):
|
||||||
if self.total_gain_loss == 0:
|
if self.total_gain_loss == 0:
|
||||||
return
|
return
|
||||||
|
|||||||
@ -12,6 +12,7 @@ from frappe.model.document import Document
|
|||||||
class FiscalYearIncorrectDate(frappe.ValidationError): pass
|
class FiscalYearIncorrectDate(frappe.ValidationError): pass
|
||||||
|
|
||||||
class FiscalYear(Document):
|
class FiscalYear(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def set_as_default(self):
|
def set_as_default(self):
|
||||||
frappe.db.set_value("Global Defaults", None, "current_fiscal_year", self.name)
|
frappe.db.set_value("Global Defaults", None, "current_fiscal_year", self.name)
|
||||||
global_defaults = frappe.get_doc("Global Defaults")
|
global_defaults = frappe.get_doc("Global Defaults")
|
||||||
|
|||||||
@ -290,4 +290,8 @@ def rename_temporarily_named_docs(doctype):
|
|||||||
oldname = doc.name
|
oldname = doc.name
|
||||||
set_name_from_naming_options(frappe.get_meta(doctype).autoname, doc)
|
set_name_from_naming_options(frappe.get_meta(doctype).autoname, doc)
|
||||||
newname = doc.name
|
newname = doc.name
|
||||||
frappe.db.sql("""UPDATE `tab{}` SET name = %s, to_rename = 0 where name = %s""".format(doctype), (newname, oldname))
|
frappe.db.sql(
|
||||||
|
"UPDATE `tab{}` SET name = %s, to_rename = 0 where name = %s".format(doctype),
|
||||||
|
(newname, oldname),
|
||||||
|
auto_commit=True
|
||||||
|
)
|
||||||
|
|||||||
@ -125,6 +125,7 @@ class InvoiceDiscounting(AccountsController):
|
|||||||
|
|
||||||
make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding='No')
|
make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding='No')
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def create_disbursement_entry(self):
|
def create_disbursement_entry(self):
|
||||||
je = frappe.new_doc("Journal Entry")
|
je = frappe.new_doc("Journal Entry")
|
||||||
je.voucher_type = 'Journal Entry'
|
je.voucher_type = 'Journal Entry'
|
||||||
@ -174,6 +175,7 @@ class InvoiceDiscounting(AccountsController):
|
|||||||
|
|
||||||
return je
|
return je
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def close_loan(self):
|
def close_loan(self):
|
||||||
je = frappe.new_doc("Journal Entry")
|
je = frappe.new_doc("Journal Entry")
|
||||||
je.voucher_type = 'Journal Entry'
|
je.voucher_type = 'Journal Entry'
|
||||||
|
|||||||
@ -564,6 +564,7 @@ class JournalEntry(AccountsController):
|
|||||||
if gl_map:
|
if gl_map:
|
||||||
make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj, update_outstanding=update_outstanding)
|
make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj, update_outstanding=update_outstanding)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_balance(self):
|
def get_balance(self):
|
||||||
if not self.get('accounts'):
|
if not self.get('accounts'):
|
||||||
msgprint(_("'Entries' cannot be empty"), raise_exception=True)
|
msgprint(_("'Entries' cannot be empty"), raise_exception=True)
|
||||||
|
|||||||
@ -8,6 +8,7 @@ from frappe.utils import (flt, add_months)
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class MonthlyDistribution(Document):
|
class MonthlyDistribution(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def get_months(self):
|
def get_months(self):
|
||||||
month_list = ['January','February','March','April','May','June','July','August','September',
|
month_list = ['January','February','March','April','May','June','July','August','September',
|
||||||
'October','November','December']
|
'October','November','December']
|
||||||
|
|||||||
@ -167,6 +167,7 @@ class OpeningInvoiceCreationTool(Document):
|
|||||||
|
|
||||||
return invoice
|
return invoice
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def make_invoices(self):
|
def make_invoices(self):
|
||||||
self.validate_company()
|
self.validate_company()
|
||||||
invoices = self.get_invoices()
|
invoices = self.get_invoices()
|
||||||
|
|||||||
@ -637,13 +637,13 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
let to_field = fields[key][1];
|
let to_field = fields[key][1];
|
||||||
|
|
||||||
if (filters[from_field] && !filters[to_field]) {
|
if (filters[from_field] && !filters[to_field]) {
|
||||||
frappe.throw(__("Error: {0} is mandatory field",
|
frappe.throw(
|
||||||
[to_field.replace(/_/g, " ")]
|
__("Error: {0} is mandatory field", [to_field.replace(/_/g, " ")])
|
||||||
));
|
);
|
||||||
} else if (filters[from_field] && filters[from_field] > filters[to_field]) {
|
} else if (filters[from_field] && filters[from_field] > filters[to_field]) {
|
||||||
frappe.throw(__("{0}: {1} must be less than {2}",
|
frappe.throw(
|
||||||
[key, from_field.replace(/_/g, " "), to_field.replace(/_/g, " ")]
|
__("{0}: {1} must be less than {2}", [key, from_field.replace(/_/g, " "), to_field.replace(/_/g, " ")])
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -692,6 +692,8 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
c.total_amount = d.invoice_amount;
|
c.total_amount = d.invoice_amount;
|
||||||
c.outstanding_amount = d.outstanding_amount;
|
c.outstanding_amount = d.outstanding_amount;
|
||||||
c.bill_no = d.bill_no;
|
c.bill_no = d.bill_no;
|
||||||
|
c.payment_term = d.payment_term;
|
||||||
|
c.allocated_amount = d.allocated_amount;
|
||||||
|
|
||||||
if(!in_list(["Sales Order", "Purchase Order", "Expense Claim", "Fees"], d.voucher_type)) {
|
if(!in_list(["Sales Order", "Purchase Order", "Expense Claim", "Fees"], d.voucher_type)) {
|
||||||
if(flt(d.outstanding_amount) > 0)
|
if(flt(d.outstanding_amount) > 0)
|
||||||
@ -774,12 +776,15 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
} else if (in_list(["Customer", "Supplier"], frm.doc.party_type)) {
|
} else if (in_list(["Customer", "Supplier"], frm.doc.party_type)) {
|
||||||
if(paid_amount > total_negative_outstanding) {
|
if(paid_amount > total_negative_outstanding) {
|
||||||
if(total_negative_outstanding == 0) {
|
if(total_negative_outstanding == 0) {
|
||||||
frappe.msgprint(__("Cannot {0} {1} {2} without any negative outstanding invoice",
|
frappe.msgprint(
|
||||||
[frm.doc.payment_type,
|
__("Cannot {0} {1} {2} without any negative outstanding invoice", [frm.doc.payment_type,
|
||||||
(frm.doc.party_type=="Customer" ? "to" : "from"), frm.doc.party_type]));
|
(frm.doc.party_type=="Customer" ? "to" : "from"), frm.doc.party_type])
|
||||||
|
);
|
||||||
return false
|
return false
|
||||||
} else {
|
} else {
|
||||||
frappe.msgprint(__("Paid Amount cannot be greater than total negative outstanding amount {0}", [total_negative_outstanding]));
|
frappe.msgprint(
|
||||||
|
__("Paid Amount cannot be greater than total negative outstanding amount {0}", [total_negative_outstanding])
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -791,10 +796,13 @@ 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
|
if (frappe.flags.allocate_payment_amount == 0) {
|
||||||
if(frappe.flags.allocate_payment_amount != 0){
|
//If allocate payment amount checkbox is unchecked, set zero to allocate amount
|
||||||
if(row.outstanding_amount > 0 && allocated_positive_outstanding > 0) {
|
row.allocated_amount = 0;
|
||||||
if(row.outstanding_amount >= allocated_positive_outstanding) {
|
|
||||||
|
} else if (frappe.flags.allocate_payment_amount != 0 && !row.allocated_amount) {
|
||||||
|
if (row.outstanding_amount > 0 && allocated_positive_outstanding > 0) {
|
||||||
|
if (row.outstanding_amount >= allocated_positive_outstanding) {
|
||||||
row.allocated_amount = allocated_positive_outstanding;
|
row.allocated_amount = allocated_positive_outstanding;
|
||||||
} else {
|
} else {
|
||||||
row.allocated_amount = row.outstanding_amount;
|
row.allocated_amount = row.outstanding_amount;
|
||||||
@ -802,9 +810,11 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
|
|
||||||
allocated_positive_outstanding -= flt(row.allocated_amount);
|
allocated_positive_outstanding -= flt(row.allocated_amount);
|
||||||
} else if (row.outstanding_amount < 0 && allocated_negative_outstanding) {
|
} else if (row.outstanding_amount < 0 && allocated_negative_outstanding) {
|
||||||
if(Math.abs(row.outstanding_amount) >= allocated_negative_outstanding)
|
if (Math.abs(row.outstanding_amount) >= allocated_negative_outstanding) {
|
||||||
row.allocated_amount = -1*allocated_negative_outstanding;
|
row.allocated_amount = -1*allocated_negative_outstanding;
|
||||||
else row.allocated_amount = row.outstanding_amount;
|
} else {
|
||||||
|
row.allocated_amount = row.outstanding_amount;
|
||||||
|
};
|
||||||
|
|
||||||
allocated_negative_outstanding -= Math.abs(flt(row.allocated_amount));
|
allocated_negative_outstanding -= Math.abs(flt(row.allocated_amount));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -333,33 +333,50 @@ class PaymentEntry(AccountsController):
|
|||||||
invoice_payment_amount_map = {}
|
invoice_payment_amount_map = {}
|
||||||
invoice_paid_amount_map = {}
|
invoice_paid_amount_map = {}
|
||||||
|
|
||||||
for reference in self.get('references'):
|
for ref in self.get('references'):
|
||||||
if reference.payment_term and reference.reference_name:
|
if ref.payment_term and ref.reference_name:
|
||||||
key = (reference.payment_term, reference.reference_name)
|
key = (ref.payment_term, ref.reference_name)
|
||||||
invoice_payment_amount_map.setdefault(key, 0.0)
|
invoice_payment_amount_map.setdefault(key, 0.0)
|
||||||
invoice_payment_amount_map[key] += reference.allocated_amount
|
invoice_payment_amount_map[key] += ref.allocated_amount
|
||||||
|
|
||||||
if not invoice_paid_amount_map.get(key):
|
if not invoice_paid_amount_map.get(key):
|
||||||
payment_schedule = frappe.get_all('Payment Schedule', filters={'parent': reference.reference_name},
|
payment_schedule = frappe.get_all(
|
||||||
fields=['paid_amount', 'payment_amount', 'payment_term'])
|
'Payment Schedule',
|
||||||
|
filters={'parent': ref.reference_name},
|
||||||
|
fields=['paid_amount', 'payment_amount', 'payment_term', 'discount', 'outstanding']
|
||||||
|
)
|
||||||
for term in payment_schedule:
|
for term in payment_schedule:
|
||||||
invoice_key = (term.payment_term, reference.reference_name)
|
invoice_key = (term.payment_term, ref.reference_name)
|
||||||
invoice_paid_amount_map.setdefault(invoice_key, {})
|
invoice_paid_amount_map.setdefault(invoice_key, {})
|
||||||
invoice_paid_amount_map[invoice_key]['outstanding'] = term.payment_amount - term.paid_amount
|
invoice_paid_amount_map[invoice_key]['outstanding'] = term.outstanding
|
||||||
|
invoice_paid_amount_map[invoice_key]['discounted_amt'] = ref.total_amount * (term.discount / 100)
|
||||||
|
|
||||||
for key, amount in iteritems(invoice_payment_amount_map):
|
for key, allocated_amount in iteritems(invoice_payment_amount_map):
|
||||||
if cancel:
|
|
||||||
frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` - %s
|
|
||||||
WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0]))
|
|
||||||
else:
|
|
||||||
outstanding = flt(invoice_paid_amount_map.get(key, {}).get('outstanding'))
|
outstanding = flt(invoice_paid_amount_map.get(key, {}).get('outstanding'))
|
||||||
|
discounted_amt = flt(invoice_paid_amount_map.get(key, {}).get('discounted_amt'))
|
||||||
|
|
||||||
if amount > outstanding:
|
if cancel:
|
||||||
|
frappe.db.sql("""
|
||||||
|
UPDATE `tabPayment Schedule`
|
||||||
|
SET
|
||||||
|
paid_amount = `paid_amount` - %s,
|
||||||
|
discounted_amount = `discounted_amount` - %s,
|
||||||
|
outstanding = `outstanding` + %s
|
||||||
|
WHERE parent = %s and payment_term = %s""",
|
||||||
|
(allocated_amount - discounted_amt, discounted_amt, allocated_amount, key[1], key[0]))
|
||||||
|
else:
|
||||||
|
if allocated_amount > outstanding:
|
||||||
frappe.throw(_('Cannot allocate more than {0} against payment term {1}').format(outstanding, key[0]))
|
frappe.throw(_('Cannot allocate more than {0} against payment term {1}').format(outstanding, key[0]))
|
||||||
|
|
||||||
if amount and outstanding:
|
if allocated_amount and outstanding:
|
||||||
frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` + %s
|
frappe.db.sql("""
|
||||||
WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0]))
|
UPDATE `tabPayment Schedule`
|
||||||
|
SET
|
||||||
|
paid_amount = `paid_amount` + %s,
|
||||||
|
discounted_amount = `discounted_amount` + %s,
|
||||||
|
outstanding = `outstanding` - %s
|
||||||
|
WHERE parent = %s and payment_term = %s""",
|
||||||
|
(allocated_amount - discounted_amt, discounted_amt, allocated_amount, key[1], key[0]))
|
||||||
|
|
||||||
def set_status(self):
|
def set_status(self):
|
||||||
if self.docstatus == 2:
|
if self.docstatus == 2:
|
||||||
@ -708,6 +725,8 @@ def get_outstanding_reference_documents(args):
|
|||||||
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"), filters=args, condition=condition)
|
args.get("party_account"), filters=args, condition=condition)
|
||||||
|
|
||||||
|
outstanding_invoices = split_invoices_based_on_payment_terms(outstanding_invoices)
|
||||||
|
|
||||||
for d in outstanding_invoices:
|
for d in outstanding_invoices:
|
||||||
d["exchange_rate"] = 1
|
d["exchange_rate"] = 1
|
||||||
if party_account_currency != company_currency:
|
if party_account_currency != company_currency:
|
||||||
@ -735,6 +754,46 @@ def get_outstanding_reference_documents(args):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def split_invoices_based_on_payment_terms(outstanding_invoices):
|
||||||
|
invoice_ref_based_on_payment_terms = {}
|
||||||
|
for idx, d in enumerate(outstanding_invoices):
|
||||||
|
if d.voucher_type in ['Sales Invoice', 'Purchase Invoice']:
|
||||||
|
payment_term_template = frappe.db.get_value(d.voucher_type, d.voucher_no, 'payment_terms_template')
|
||||||
|
if payment_term_template:
|
||||||
|
allocate_payment_based_on_payment_terms = frappe.db.get_value(
|
||||||
|
'Payment Terms Template', payment_term_template, 'allocate_payment_based_on_payment_terms')
|
||||||
|
if allocate_payment_based_on_payment_terms:
|
||||||
|
payment_schedule = frappe.get_all('Payment Schedule', filters={'parent': d.voucher_no}, fields=["*"])
|
||||||
|
|
||||||
|
for payment_term in payment_schedule:
|
||||||
|
if payment_term.outstanding > 0.1:
|
||||||
|
invoice_ref_based_on_payment_terms.setdefault(idx, [])
|
||||||
|
invoice_ref_based_on_payment_terms[idx].append(frappe._dict({
|
||||||
|
'due_date': d.due_date,
|
||||||
|
'currency': d.currency,
|
||||||
|
'voucher_no': d.voucher_no,
|
||||||
|
'voucher_type': d.voucher_type,
|
||||||
|
'posting_date': d.posting_date,
|
||||||
|
'invoice_amount': flt(d.invoice_amount),
|
||||||
|
'outstanding_amount': flt(d.outstanding_amount),
|
||||||
|
'payment_amount': payment_term.payment_amount,
|
||||||
|
'payment_term': payment_term.payment_term,
|
||||||
|
'allocated_amount': payment_term.outstanding
|
||||||
|
}))
|
||||||
|
|
||||||
|
if invoice_ref_based_on_payment_terms:
|
||||||
|
for idx, ref in invoice_ref_based_on_payment_terms.items():
|
||||||
|
voucher_no = outstanding_invoices[idx]['voucher_no']
|
||||||
|
voucher_type = outstanding_invoices[idx]['voucher_type']
|
||||||
|
|
||||||
|
frappe.msgprint(_("Spliting {} {} into {} rows as per payment terms").format(
|
||||||
|
voucher_type, voucher_no, len(ref)), alert=True)
|
||||||
|
|
||||||
|
outstanding_invoices.pop(idx - 1)
|
||||||
|
outstanding_invoices += invoice_ref_based_on_payment_terms[idx]
|
||||||
|
|
||||||
|
return outstanding_invoices
|
||||||
|
|
||||||
def get_orders_to_be_billed(posting_date, party_type, party,
|
def get_orders_to_be_billed(posting_date, party_type, party,
|
||||||
company, party_account_currency, company_currency, cost_center=None, filters=None):
|
company, party_account_currency, company_currency, cost_center=None, filters=None):
|
||||||
if party_type == "Customer":
|
if party_type == "Customer":
|
||||||
@ -1091,6 +1150,8 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
|||||||
paid_amount, received_amount = set_paid_amount_and_received_amount(
|
paid_amount, received_amount = set_paid_amount_and_received_amount(
|
||||||
dt, party_account_currency, bank, outstanding_amount, payment_type, bank_amount, doc)
|
dt, party_account_currency, bank, outstanding_amount, payment_type, bank_amount, doc)
|
||||||
|
|
||||||
|
paid_amount, received_amount, discount_amount = apply_early_payment_discount(paid_amount, received_amount, doc)
|
||||||
|
|
||||||
pe = frappe.new_doc("Payment Entry")
|
pe = frappe.new_doc("Payment Entry")
|
||||||
pe.payment_type = payment_type
|
pe.payment_type = payment_type
|
||||||
pe.company = doc.company
|
pe.company = doc.company
|
||||||
@ -1160,11 +1221,20 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
|||||||
|
|
||||||
pe.setup_party_account_field()
|
pe.setup_party_account_field()
|
||||||
pe.set_missing_values()
|
pe.set_missing_values()
|
||||||
|
|
||||||
if party_account and bank:
|
if party_account and bank:
|
||||||
if dt == "Employee Advance":
|
if dt == "Employee Advance":
|
||||||
reference_doc = doc
|
reference_doc = doc
|
||||||
pe.set_exchange_rate(ref_doc=reference_doc)
|
pe.set_exchange_rate(ref_doc=reference_doc)
|
||||||
pe.set_amounts()
|
pe.set_amounts()
|
||||||
|
if discount_amount:
|
||||||
|
pe.set_gain_or_loss(account_details={
|
||||||
|
'account': frappe.get_cached_value('Company', pe.company, "default_discount_account"),
|
||||||
|
'cost_center': pe.cost_center or frappe.get_cached_value('Company', pe.company, "cost_center"),
|
||||||
|
'amount': discount_amount * (-1 if payment_type == "Pay" else 1)
|
||||||
|
})
|
||||||
|
pe.set_difference_amount()
|
||||||
|
|
||||||
return pe
|
return pe
|
||||||
|
|
||||||
def get_bank_cash_account(doc, bank_account):
|
def get_bank_cash_account(doc, bank_account):
|
||||||
@ -1285,6 +1355,33 @@ def set_paid_amount_and_received_amount(dt, party_account_currency, bank, outsta
|
|||||||
paid_amount = received_amount * doc.get('exchange_rate', 1)
|
paid_amount = received_amount * doc.get('exchange_rate', 1)
|
||||||
return paid_amount, received_amount
|
return paid_amount, received_amount
|
||||||
|
|
||||||
|
def apply_early_payment_discount(paid_amount, received_amount, doc):
|
||||||
|
total_discount = 0
|
||||||
|
if doc.doctype in ['Sales Invoice', 'Purchase Invoice'] and doc.payment_schedule:
|
||||||
|
for term in doc.payment_schedule:
|
||||||
|
if not term.discounted_amount and term.discount and getdate(nowdate()) <= term.discount_date:
|
||||||
|
if term.discount_type == 'Percentage':
|
||||||
|
discount_amount = flt(doc.get('grand_total')) * (term.discount / 100)
|
||||||
|
else:
|
||||||
|
discount_amount = term.discount
|
||||||
|
|
||||||
|
discount_amount_in_foreign_currency = discount_amount * doc.get('conversion_rate', 1)
|
||||||
|
|
||||||
|
if doc.doctype == 'Sales Invoice':
|
||||||
|
paid_amount -= discount_amount
|
||||||
|
received_amount -= discount_amount_in_foreign_currency
|
||||||
|
else:
|
||||||
|
received_amount -= discount_amount
|
||||||
|
paid_amount -= discount_amount_in_foreign_currency
|
||||||
|
|
||||||
|
total_discount += discount_amount
|
||||||
|
|
||||||
|
if total_discount:
|
||||||
|
money = frappe.utils.fmt_money(total_discount, currency=doc.get('currency'))
|
||||||
|
frappe.msgprint(_("Discount of {} applied as per Payment Term").format(money), alert=1)
|
||||||
|
|
||||||
|
return paid_amount, received_amount, total_discount
|
||||||
|
|
||||||
def get_reference_as_per_payment_terms(payment_schedule, dt, dn, doc, grand_total, outstanding_amount):
|
def get_reference_as_per_payment_terms(payment_schedule, dt, dn, doc, grand_total, outstanding_amount):
|
||||||
references = []
|
references = []
|
||||||
for payment_term in payment_schedule:
|
for payment_term in payment_schedule:
|
||||||
|
|||||||
@ -193,6 +193,34 @@ class TestPaymentEntry(unittest.TestCase):
|
|||||||
self.assertEqual(si.payment_schedule[0].paid_amount, 200.0)
|
self.assertEqual(si.payment_schedule[0].paid_amount, 200.0)
|
||||||
self.assertEqual(si.payment_schedule[1].paid_amount, 36.0)
|
self.assertEqual(si.payment_schedule[1].paid_amount, 36.0)
|
||||||
|
|
||||||
|
def test_payment_entry_against_payment_terms_with_discount(self):
|
||||||
|
si = create_sales_invoice(do_not_save=1, qty=1, rate=200)
|
||||||
|
create_payment_terms_template_with_discount()
|
||||||
|
si.payment_terms_template = 'Test Discount Template'
|
||||||
|
|
||||||
|
frappe.db.set_value('Company', si.company, 'default_discount_account', 'Write Off - _TC')
|
||||||
|
|
||||||
|
si.append('taxes', {
|
||||||
|
"charge_type": "On Net Total",
|
||||||
|
"account_head": "_Test Account Service Tax - _TC",
|
||||||
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
|
"description": "Service Tax",
|
||||||
|
"rate": 18
|
||||||
|
})
|
||||||
|
si.save()
|
||||||
|
|
||||||
|
si.submit()
|
||||||
|
|
||||||
|
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Cash - _TC")
|
||||||
|
pe.submit()
|
||||||
|
si.load_from_db()
|
||||||
|
|
||||||
|
self.assertEqual(pe.references[0].payment_term, '30 Credit Days with 10% Discount')
|
||||||
|
self.assertEqual(si.payment_schedule[0].payment_amount, 236.0)
|
||||||
|
self.assertEqual(si.payment_schedule[0].paid_amount, 212.40)
|
||||||
|
self.assertEqual(si.payment_schedule[0].outstanding, 0)
|
||||||
|
self.assertEqual(si.payment_schedule[0].discounted_amount, 23.6)
|
||||||
|
|
||||||
|
|
||||||
def test_payment_against_purchase_invoice_to_check_status(self):
|
def test_payment_against_purchase_invoice_to_check_status(self):
|
||||||
pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC",
|
pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC",
|
||||||
@ -591,6 +619,26 @@ def create_payment_terms_template():
|
|||||||
}]
|
}]
|
||||||
}).insert()
|
}).insert()
|
||||||
|
|
||||||
|
def create_payment_terms_template_with_discount():
|
||||||
|
|
||||||
|
create_payment_term('30 Credit Days with 10% Discount')
|
||||||
|
|
||||||
|
if not frappe.db.exists('Payment Terms Template', 'Test Discount Template'):
|
||||||
|
payment_term_template = frappe.get_doc({
|
||||||
|
'doctype': 'Payment Terms Template',
|
||||||
|
'template_name': 'Test Discount Template',
|
||||||
|
'allocate_payment_based_on_payment_terms': 1,
|
||||||
|
'terms': [{
|
||||||
|
'doctype': 'Payment Terms Template Detail',
|
||||||
|
'payment_term': '30 Credit Days with 10% Discount',
|
||||||
|
'invoice_portion': 100,
|
||||||
|
'credit_days_based_on': 'Day(s) after invoice date',
|
||||||
|
'credit_days': 2,
|
||||||
|
'discount': 10,
|
||||||
|
'discount_validity_based_on': 'Day(s) after invoice date',
|
||||||
|
'discount_validity': 1
|
||||||
|
}]
|
||||||
|
}).insert()
|
||||||
|
|
||||||
def create_payment_term(name):
|
def create_payment_term(name):
|
||||||
if not frappe.db.exists('Payment Term', name):
|
if not frappe.db.exists('Payment Term', name):
|
||||||
|
|||||||
@ -58,7 +58,7 @@
|
|||||||
"fieldname": "total_amount",
|
"fieldname": "total_amount",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Total Amount",
|
"label": "Grand Total",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
@ -92,9 +92,10 @@
|
|||||||
"options": "Payment Term"
|
"options": "Payment Term"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-03-13 12:07:19.362539",
|
"modified": "2021-02-10 11:25:47.144392",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Entry Reference",
|
"name": "Payment Entry Reference",
|
||||||
|
|||||||
@ -11,6 +11,7 @@ from erpnext.accounts.utils import (get_outstanding_invoices,
|
|||||||
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):
|
||||||
|
@frappe.whitelist()
|
||||||
def get_unreconciled_entries(self):
|
def get_unreconciled_entries(self):
|
||||||
self.get_nonreconciled_payment_entries()
|
self.get_nonreconciled_payment_entries()
|
||||||
self.get_invoice_entries()
|
self.get_invoice_entries()
|
||||||
@ -147,6 +148,7 @@ class PaymentReconciliation(Document):
|
|||||||
ent.currency = e.get('currency')
|
ent.currency = e.get('currency')
|
||||||
ent.outstanding_amount = e.get('outstanding_amount')
|
ent.outstanding_amount = e.get('outstanding_amount')
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def reconcile(self, args):
|
def reconcile(self, args):
|
||||||
for e in self.get('payments'):
|
for e in self.get('payments'):
|
||||||
e.invoice_type = None
|
e.invoice_type = None
|
||||||
@ -197,6 +199,7 @@ class PaymentReconciliation(Document):
|
|||||||
'difference_account': row.difference_account
|
'difference_account': row.difference_account
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_difference_amount(self, child_row):
|
def get_difference_amount(self, child_row):
|
||||||
if child_row.get("reference_type") != 'Payment Entry': return
|
if child_row.get("reference_type") != 'Payment Entry': return
|
||||||
|
|
||||||
|
|||||||
@ -6,11 +6,23 @@
|
|||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"payment_term",
|
"payment_term",
|
||||||
|
"section_break_15",
|
||||||
"description",
|
"description",
|
||||||
|
"section_break_4",
|
||||||
"due_date",
|
"due_date",
|
||||||
"invoice_portion",
|
|
||||||
"payment_amount",
|
|
||||||
"mode_of_payment",
|
"mode_of_payment",
|
||||||
|
"column_break_5",
|
||||||
|
"invoice_portion",
|
||||||
|
"section_break_6",
|
||||||
|
"discount_type",
|
||||||
|
"discount_date",
|
||||||
|
"column_break_9",
|
||||||
|
"discount",
|
||||||
|
"section_break_9",
|
||||||
|
"payment_amount",
|
||||||
|
"discounted_amount",
|
||||||
|
"column_break_3",
|
||||||
|
"outstanding",
|
||||||
"paid_amount"
|
"paid_amount"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
@ -25,6 +37,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
|
"fetch_from": "payment_term.description",
|
||||||
"fieldname": "description",
|
"fieldname": "description",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
@ -62,14 +75,82 @@
|
|||||||
"options": "Mode of Payment"
|
"options": "Mode of Payment"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "paid_amount",
|
||||||
"fieldname": "paid_amount",
|
"fieldname": "paid_amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Paid Amount"
|
"label": "Paid Amount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_3",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "discounted_amount",
|
||||||
|
"fieldname": "discounted_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Discounted Amount",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "payment_amount",
|
||||||
|
"fieldname": "outstanding",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Outstanding",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_5",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "discount",
|
||||||
|
"fieldname": "discount_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "Discount Date",
|
||||||
|
"mandatory_depends_on": "discount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "Percentage",
|
||||||
|
"fetch_from": "payment_term.discount_type",
|
||||||
|
"fieldname": "discount_type",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Discount Type",
|
||||||
|
"options": "Percentage\nAmount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "payment_term.discount",
|
||||||
|
"fieldname": "discount",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Discount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_9",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapsible": 1,
|
||||||
|
"fieldname": "section_break_15",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Description"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_6",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_9",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_4",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-03-13 17:58:24.729526",
|
"modified": "2021-02-15 21:03:12.540546",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Schedule",
|
"name": "Payment Schedule",
|
||||||
|
|||||||
@ -1,2 +1,22 @@
|
|||||||
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
frappe.ui.form.on('Payment Term', {
|
||||||
|
onload(frm) {
|
||||||
|
frm.trigger('set_dynamic_description');
|
||||||
|
},
|
||||||
|
discount(frm) {
|
||||||
|
frm.trigger('set_dynamic_description');
|
||||||
|
},
|
||||||
|
discount_type(frm) {
|
||||||
|
frm.trigger('set_dynamic_description');
|
||||||
|
},
|
||||||
|
set_dynamic_description(frm) {
|
||||||
|
if (frm.doc.discount) {
|
||||||
|
let description = __("{0}% of total invoice value will be given as discount.", [frm.doc.discount]);
|
||||||
|
if (frm.doc.discount_type == 'Amount') {
|
||||||
|
description = __("{0} will be given as discount.", [fmt_money(frm.doc.discount)]);
|
||||||
|
}
|
||||||
|
frm.set_df_property("discount", "description", description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
@ -1,386 +1,166 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"allow_rename": 1,
|
"allow_rename": 1,
|
||||||
"autoname": "field:payment_term_name",
|
"autoname": "field:payment_term_name",
|
||||||
"beta": 0,
|
|
||||||
"creation": "2017-08-10 15:24:54.876365",
|
"creation": "2017-08-10 15:24:54.876365",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "",
|
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"payment_term_name",
|
||||||
|
"invoice_portion",
|
||||||
|
"mode_of_payment",
|
||||||
|
"column_break_3",
|
||||||
|
"due_date_based_on",
|
||||||
|
"credit_days",
|
||||||
|
"credit_months",
|
||||||
|
"section_break_8",
|
||||||
|
"discount_type",
|
||||||
|
"discount",
|
||||||
|
"column_break_11",
|
||||||
|
"discount_validity_based_on",
|
||||||
|
"discount_validity",
|
||||||
|
"section_break_6",
|
||||||
|
"description"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 1,
|
"bold": 1,
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "payment_term_name",
|
"fieldname": "payment_term_name",
|
||||||
"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": "Payment Term Name",
|
"label": "Payment Term Name",
|
||||||
"length": 0,
|
"unique": 1
|
||||||
"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
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Provide the invoice portion in percent",
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 1,
|
"bold": 1,
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "invoice_portion",
|
"fieldname": "invoice_portion",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
"label": "Invoice Portion (%)"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Invoice Portion",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "mode_of_payment",
|
"fieldname": "mode_of_payment",
|
||||||
"fieldtype": "Link",
|
"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": "Mode of Payment",
|
"label": "Mode of Payment",
|
||||||
"length": 0,
|
"options": "Mode of Payment"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Mode of Payment",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break_3",
|
"fieldname": "column_break_3",
|
||||||
"fieldtype": "Column Break",
|
"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_on_submit": 0,
|
|
||||||
"bold": 1,
|
"bold": 1,
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "due_date_based_on",
|
"fieldname": "due_date_based_on",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Due Date Based On",
|
"label": "Due Date Based On",
|
||||||
"length": 0,
|
"options": "Day(s) after invoice date\nDay(s) after the end of the invoice month\nMonth(s) after the end of the invoice month"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Day(s) after invoice date\nDay(s) after the end of the invoice month\nMonth(s) after the end of the invoice month",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Give number of days according to prior selection",
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 1,
|
"bold": 1,
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"depends_on": "eval:in_list(['Day(s) after invoice date', 'Day(s) after the end of the invoice month'], doc.due_date_based_on)",
|
"depends_on": "eval:in_list(['Day(s) after invoice date', 'Day(s) after the end of the invoice month'], doc.due_date_based_on)",
|
||||||
"fieldname": "credit_days",
|
"fieldname": "credit_days",
|
||||||
"fieldtype": "Int",
|
"fieldtype": "Int",
|
||||||
"hidden": 0,
|
"label": "Credit Days"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Credit Days",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"depends_on": "eval:doc.due_date_based_on=='Month(s) after the end of the invoice month'",
|
"depends_on": "eval:doc.due_date_based_on=='Month(s) after the end of the invoice month'",
|
||||||
"fieldname": "credit_months",
|
"fieldname": "credit_months",
|
||||||
"fieldtype": "Int",
|
"fieldtype": "Int",
|
||||||
"hidden": 0,
|
"label": "Credit Months"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Credit Months",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "section_break_6",
|
"fieldname": "section_break_6",
|
||||||
"fieldtype": "Section Break",
|
"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_on_submit": 0,
|
|
||||||
"bold": 1,
|
"bold": 1,
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "description",
|
"fieldname": "description",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"hidden": 0,
|
"label": "Description"
|
||||||
"ignore_user_permissions": 0,
|
},
|
||||||
"ignore_xss_filter": 0,
|
{
|
||||||
"in_filter": 0,
|
"fieldname": "section_break_8",
|
||||||
"in_global_search": 0,
|
"fieldtype": "Section Break",
|
||||||
"in_list_view": 0,
|
"label": "Discount Settings"
|
||||||
"in_standard_filter": 0,
|
},
|
||||||
"label": "Description",
|
{
|
||||||
"length": 0,
|
"default": "Percentage",
|
||||||
"no_copy": 0,
|
"fieldname": "discount_type",
|
||||||
"permlevel": 0,
|
"fieldtype": "Select",
|
||||||
"precision": "",
|
"label": "Discount Type",
|
||||||
"print_hide": 0,
|
"options": "Percentage\nAmount"
|
||||||
"print_hide_if_no_value": 0,
|
},
|
||||||
"read_only": 0,
|
{
|
||||||
"remember_last_selected_value": 0,
|
"fieldname": "discount",
|
||||||
"report_hide": 0,
|
"fieldtype": "Float",
|
||||||
"reqd": 0,
|
"label": "Discount"
|
||||||
"search_index": 0,
|
},
|
||||||
"set_only_once": 0,
|
{
|
||||||
"translatable": 0,
|
"default": "Day(s) after invoice date",
|
||||||
"unique": 0
|
"depends_on": "discount",
|
||||||
|
"fieldname": "discount_validity_based_on",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Discount Validity Based On",
|
||||||
|
"options": "Day(s) after invoice date\nDay(s) after the end of the invoice month\nMonth(s) after the end of the invoice month"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "discount",
|
||||||
|
"fieldname": "discount_validity",
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"label": "Discount Validity",
|
||||||
|
"mandatory_depends_on": "discount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_11",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"links": [],
|
||||||
"hide_heading": 0,
|
"modified": "2021-02-15 20:30:56.256403",
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 0,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2020-10-14 10:47:32.830478",
|
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Term",
|
"name": "Payment Term",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"apply_user_permissions": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"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,
|
|
||||||
"apply_user_permissions": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Accounts Manager",
|
"role": "Accounts Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"apply_user_permissions": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"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
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1,
|
"track_changes": 1
|
||||||
"track_seen": 0
|
|
||||||
}
|
}
|
||||||
@ -3,11 +3,6 @@
|
|||||||
|
|
||||||
frappe.ui.form.on('Payment Terms Template', {
|
frappe.ui.form.on('Payment Terms Template', {
|
||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
frm.add_fetch("payment_term", "description", "description");
|
|
||||||
frm.add_fetch("payment_term", "invoice_portion", "invoice_portion");
|
|
||||||
frm.add_fetch("payment_term", "due_date_based_on", "due_date_based_on");
|
|
||||||
frm.add_fetch("payment_term", "credit_days", "credit_days");
|
|
||||||
frm.add_fetch("payment_term", "credit_months", "credit_months");
|
|
||||||
frm.add_fetch("payment_term", "mode_of_payment", "mode_of_payment");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -13,7 +13,6 @@ from frappe import _
|
|||||||
class PaymentTermsTemplate(Document):
|
class PaymentTermsTemplate(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_invoice_portion()
|
self.validate_invoice_portion()
|
||||||
self.validate_credit_days()
|
|
||||||
self.check_duplicate_terms()
|
self.check_duplicate_terms()
|
||||||
|
|
||||||
def validate_invoice_portion(self):
|
def validate_invoice_portion(self):
|
||||||
@ -24,11 +23,6 @@ class PaymentTermsTemplate(Document):
|
|||||||
if flt(total_portion, 2) != 100.00:
|
if flt(total_portion, 2) != 100.00:
|
||||||
frappe.msgprint(_('Combined invoice portion must equal 100%'), raise_exception=1, indicator='red')
|
frappe.msgprint(_('Combined invoice portion must equal 100%'), raise_exception=1, indicator='red')
|
||||||
|
|
||||||
def validate_credit_days(self):
|
|
||||||
for term in self.terms:
|
|
||||||
if cint(term.credit_days) < 0:
|
|
||||||
frappe.msgprint(_('Credit Days cannot be a negative number'), raise_exception=1, indicator='red')
|
|
||||||
|
|
||||||
def check_duplicate_terms(self):
|
def check_duplicate_terms(self):
|
||||||
terms = []
|
terms = []
|
||||||
for term in self.terms:
|
for term in self.terms:
|
||||||
|
|||||||
@ -1,278 +1,164 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"autoname": "",
|
|
||||||
"beta": 0,
|
|
||||||
"creation": "2017-08-10 15:34:09.409562",
|
"creation": "2017-08-10 15:34:09.409562",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "",
|
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"payment_term",
|
||||||
|
"section_break_13",
|
||||||
|
"description",
|
||||||
|
"section_break_4",
|
||||||
|
"invoice_portion",
|
||||||
|
"mode_of_payment",
|
||||||
|
"column_break_3",
|
||||||
|
"due_date_based_on",
|
||||||
|
"credit_days",
|
||||||
|
"credit_months",
|
||||||
|
"section_break_8",
|
||||||
|
"discount_type",
|
||||||
|
"discount",
|
||||||
|
"column_break_11",
|
||||||
|
"discount_validity_based_on",
|
||||||
|
"discount_validity"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
"fieldname": "payment_term",
|
"fieldname": "payment_term",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"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": "Payment Term",
|
"label": "Payment Term",
|
||||||
"length": 0,
|
"options": "Payment Term"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Payment Term",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
|
"fetch_from": "payment_term.description",
|
||||||
"fieldname": "description",
|
"fieldname": "description",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
"label": "Description"
|
||||||
"label": "Description",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
"default": "0",
|
"fetch_from": "payment_term.invoice_portion",
|
||||||
|
"fetch_if_empty": 1,
|
||||||
"fieldname": "invoice_portion",
|
"fieldname": "invoice_portion",
|
||||||
"fieldtype": "Percent",
|
"fieldtype": "Float",
|
||||||
"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": "Invoice Portion (%)",
|
||||||
"label": "Invoice Portion",
|
"reqd": 1
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
|
"fetch_from": "payment_term.due_date_based_on",
|
||||||
|
"fetch_if_empty": 1,
|
||||||
"fieldname": "due_date_based_on",
|
"fieldname": "due_date_based_on",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"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": "Due Date Based On",
|
"label": "Due Date Based On",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Day(s) after invoice date\nDay(s) after the end of the invoice month\nMonth(s) after the end of the invoice month",
|
"options": "Day(s) after invoice date\nDay(s) after the end of the invoice month\nMonth(s) after the end of the invoice month",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"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": 2,
|
"columns": 2,
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"depends_on": "eval:in_list(['Day(s) after invoice date', 'Day(s) after the end of the invoice month'], doc.due_date_based_on)",
|
"depends_on": "eval:in_list(['Day(s) after invoice date', 'Day(s) after the end of the invoice month'], doc.due_date_based_on)",
|
||||||
|
"fetch_from": "payment_term.credit_days",
|
||||||
|
"fetch_if_empty": 1,
|
||||||
"fieldname": "credit_days",
|
"fieldname": "credit_days",
|
||||||
"fieldtype": "Int",
|
"fieldtype": "Int",
|
||||||
"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": "Credit Days",
|
"label": "Credit Days",
|
||||||
"length": 0,
|
"non_negative": 1
|
||||||
"no_copy": 0,
|
|
||||||
"options": "",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"depends_on": "eval:doc.due_date_based_on=='Month(s) after the end of the invoice month'",
|
"depends_on": "eval:doc.due_date_based_on=='Month(s) after the end of the invoice month'",
|
||||||
|
"fetch_from": "payment_term.credit_months",
|
||||||
|
"fetch_if_empty": 1,
|
||||||
"fieldname": "credit_months",
|
"fieldname": "credit_months",
|
||||||
"fieldtype": "Int",
|
"fieldtype": "Int",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Credit Months",
|
"label": "Credit Months",
|
||||||
"length": 0,
|
"non_negative": 1
|
||||||
"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,
|
"fetch_from": "payment_term.mode_of_payment",
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "mode_of_payment",
|
"fieldname": "mode_of_payment",
|
||||||
"fieldtype": "Link",
|
"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": "Mode of Payment",
|
"label": "Mode of Payment",
|
||||||
"length": 0,
|
"options": "Mode of Payment"
|
||||||
"no_copy": 0,
|
},
|
||||||
"options": "Mode of Payment",
|
{
|
||||||
"permlevel": 0,
|
"fieldname": "column_break_3",
|
||||||
"precision": "",
|
"fieldtype": "Column Break"
|
||||||
"print_hide": 0,
|
},
|
||||||
"print_hide_if_no_value": 0,
|
{
|
||||||
"read_only": 0,
|
"fieldname": "section_break_8",
|
||||||
"remember_last_selected_value": 0,
|
"fieldtype": "Section Break",
|
||||||
"report_hide": 0,
|
"label": "Discount Settings"
|
||||||
"reqd": 0,
|
},
|
||||||
"search_index": 0,
|
{
|
||||||
"set_only_once": 0,
|
"default": "Percentage",
|
||||||
"translatable": 0,
|
"fetch_from": "payment_term.discount_type",
|
||||||
"unique": 0
|
"fetch_if_empty": 1,
|
||||||
|
"fieldname": "discount_type",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Discount Type",
|
||||||
|
"options": "Percentage\nAmount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "payment_term.discount",
|
||||||
|
"fetch_if_empty": 1,
|
||||||
|
"fieldname": "discount",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Discount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_11",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "Day(s) after invoice date",
|
||||||
|
"depends_on": "discount",
|
||||||
|
"fetch_from": "payment_term.discount_validity_based_on",
|
||||||
|
"fetch_if_empty": 1,
|
||||||
|
"fieldname": "discount_validity_based_on",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Discount Validity Based On",
|
||||||
|
"options": "Day(s) after invoice date\nDay(s) after the end of the invoice month\nMonth(s) after the end of the invoice month"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapsible": 1,
|
||||||
|
"fieldname": "section_break_13",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Description"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "discount",
|
||||||
|
"fetch_from": "payment_term.discount_validity",
|
||||||
|
"fetch_if_empty": 1,
|
||||||
|
"fieldname": "discount_validity",
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"label": "Discount Validity",
|
||||||
|
"mandatory_depends_on": "discount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_4",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"index_web_pages_for_search": 1,
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 0,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"links": [],
|
||||||
"modified": "2018-08-21 16:15:55.143025",
|
"modified": "2021-02-24 11:56:12.410807",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Terms Template Detail",
|
"name": "Payment Terms Template Detail",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1,
|
"track_changes": 1
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
||||||
@ -68,6 +68,7 @@ class POSClosingEntry(StatusUpdater):
|
|||||||
|
|
||||||
frappe.throw(error_list, title=_("Invalid POS Invoices"), as_list=True)
|
frappe.throw(error_list, title=_("Invalid POS Invoices"), as_list=True)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_payment_reconciliation_details(self):
|
def get_payment_reconciliation_details(self):
|
||||||
currency = frappe.get_cached_value('Company', self.company, "default_currency")
|
currency = frappe.get_cached_value('Company', self.company, "default_currency")
|
||||||
return frappe.render_template("erpnext/accounts/doctype/pos_closing_entry/closing_voucher_details.html",
|
return frappe.render_template("erpnext/accounts/doctype/pos_closing_entry/closing_voucher_details.html",
|
||||||
|
|||||||
@ -355,6 +355,7 @@ class POSInvoice(SalesInvoice):
|
|||||||
|
|
||||||
return profile
|
return profile
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def set_missing_values(self, for_validate=False):
|
def set_missing_values(self, for_validate=False):
|
||||||
profile = self.set_pos_fields(for_validate)
|
profile = self.set_pos_fields(for_validate)
|
||||||
|
|
||||||
@ -377,6 +378,7 @@ class POSInvoice(SalesInvoice):
|
|||||||
"allow_print_before_pay": profile.get("allow_print_before_pay")
|
"allow_print_before_pay": profile.get("allow_print_before_pay")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def reset_mode_of_payments(self):
|
def reset_mode_of_payments(self):
|
||||||
if self.pos_profile:
|
if self.pos_profile:
|
||||||
pos_profile = frappe.get_cached_doc('POS Profile', self.pos_profile)
|
pos_profile = frappe.get_cached_doc('POS Profile', self.pos_profile)
|
||||||
@ -389,6 +391,7 @@ class POSInvoice(SalesInvoice):
|
|||||||
if not pay.account:
|
if not pay.account:
|
||||||
pay.account = get_bank_cash_account(pay.mode_of_payment, self.company).get("account")
|
pay.account = get_bank_cash_account(pay.mode_of_payment, self.company).get("account")
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def create_payment_request(self):
|
def create_payment_request(self):
|
||||||
for pay in self.payments:
|
for pay in self.payments:
|
||||||
if pay.type == "Phone":
|
if pay.type == "Phone":
|
||||||
|
|||||||
@ -390,6 +390,7 @@ class SalesInvoice(SellingController):
|
|||||||
if validate_against_credit_limit:
|
if validate_against_credit_limit:
|
||||||
check_credit_limit(self.customer, self.company, bypass_credit_limit_check_at_sales_order)
|
check_credit_limit(self.customer, self.company, bypass_credit_limit_check_at_sales_order)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def set_missing_values(self, for_validate=False):
|
def set_missing_values(self, for_validate=False):
|
||||||
pos = self.set_pos_fields(for_validate)
|
pos = self.set_pos_fields(for_validate)
|
||||||
|
|
||||||
@ -729,6 +730,7 @@ class SalesInvoice(SellingController):
|
|||||||
else:
|
else:
|
||||||
self.calculate_billing_amount_for_timesheet()
|
self.calculate_billing_amount_for_timesheet()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def add_timesheet_data(self):
|
def add_timesheet_data(self):
|
||||||
self.set('timesheets', [])
|
self.set('timesheets', [])
|
||||||
if self.project:
|
if self.project:
|
||||||
@ -1286,6 +1288,7 @@ class SalesInvoice(SellingController):
|
|||||||
break
|
break
|
||||||
|
|
||||||
# Healthcare
|
# Healthcare
|
||||||
|
@frappe.whitelist()
|
||||||
def set_healthcare_services(self, checked_values):
|
def set_healthcare_services(self, checked_values):
|
||||||
self.set("items", [])
|
self.set("items", [])
|
||||||
from erpnext.stock.get_item_details import get_item_details
|
from erpnext.stock.get_item_details import get_item_details
|
||||||
|
|||||||
@ -364,7 +364,7 @@ class ReceivablePayableReport(object):
|
|||||||
payment_terms_details = frappe.db.sql("""
|
payment_terms_details = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
si.name, si.party_account_currency, si.currency, si.conversion_rate,
|
si.name, si.party_account_currency, si.currency, si.conversion_rate,
|
||||||
ps.due_date, ps.payment_amount, ps.description, ps.paid_amount
|
ps.due_date, ps.payment_amount, ps.description, ps.paid_amount, ps.discounted_amount
|
||||||
from `tab{0}` si, `tabPayment Schedule` ps
|
from `tab{0}` si, `tabPayment Schedule` ps
|
||||||
where
|
where
|
||||||
si.name = ps.parent and
|
si.name = ps.parent and
|
||||||
@ -395,13 +395,13 @@ class ReceivablePayableReport(object):
|
|||||||
"invoiced": invoiced,
|
"invoiced": invoiced,
|
||||||
"invoice_grand_total": row.invoiced,
|
"invoice_grand_total": row.invoiced,
|
||||||
"payment_term": d.description,
|
"payment_term": d.description,
|
||||||
"paid": d.paid_amount,
|
"paid": d.paid_amount + d.discounted_amount,
|
||||||
"credit_note": 0.0,
|
"credit_note": 0.0,
|
||||||
"outstanding": invoiced - d.paid_amount
|
"outstanding": invoiced - d.paid_amount - d.discounted_amount
|
||||||
}))
|
}))
|
||||||
|
|
||||||
if d.paid_amount:
|
if d.paid_amount:
|
||||||
row['paid'] -= d.paid_amount
|
row['paid'] -= d.paid_amount + d.discounted_amount
|
||||||
|
|
||||||
def allocate_closing_to_term(self, row, term, key):
|
def allocate_closing_to_term(self, row, term, key):
|
||||||
if row[key]:
|
if row[key]:
|
||||||
|
|||||||
@ -71,6 +71,7 @@ class CropCycle(Document):
|
|||||||
"exp_end_date": add_days(start_date, crop_task.get("end_day") - 1)
|
"exp_end_date": add_days(start_date, crop_task.get("end_day") - 1)
|
||||||
}).insert()
|
}).insert()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def reload_linked_analysis(self):
|
def reload_linked_analysis(self):
|
||||||
linked_doctypes = ['Soil Texture', 'Soil Analysis', 'Plant Analysis']
|
linked_doctypes = ['Soil Texture', 'Soil Analysis', 'Plant Analysis']
|
||||||
required_fields = ['location', 'name', 'collection_datetime']
|
required_fields = ['location', 'name', 'collection_datetime']
|
||||||
@ -87,6 +88,7 @@ class CropCycle(Document):
|
|||||||
frappe.publish_realtime("List of Linked Docs",
|
frappe.publish_realtime("List of Linked Docs",
|
||||||
output, user=frappe.session.user)
|
output, user=frappe.session.user)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def append_to_child(self, obj_to_append):
|
def append_to_child(self, obj_to_append):
|
||||||
for doctype in obj_to_append:
|
for doctype in obj_to_append:
|
||||||
for doc_name in set(obj_to_append[doctype]):
|
for doc_name in set(obj_to_append[doctype]):
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import frappe
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class Fertilizer(Document):
|
class Fertilizer(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def load_contents(self):
|
def load_contents(self):
|
||||||
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Fertilizer'})
|
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Fertilizer'})
|
||||||
for doc in docs:
|
for doc in docs:
|
||||||
|
|||||||
@ -8,6 +8,7 @@ from frappe.model.naming import make_autoname
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class PlantAnalysis(Document):
|
class PlantAnalysis(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def load_contents(self):
|
def load_contents(self):
|
||||||
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Plant Analysis'})
|
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Plant Analysis'})
|
||||||
for doc in docs:
|
for doc in docs:
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import frappe
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class SoilAnalysis(Document):
|
class SoilAnalysis(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def load_contents(self):
|
def load_contents(self):
|
||||||
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Soil Analysis'})
|
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Soil Analysis'})
|
||||||
for doc in docs:
|
for doc in docs:
|
||||||
|
|||||||
@ -13,6 +13,7 @@ class SoilTexture(Document):
|
|||||||
soil_edit_order = [2, 1, 0]
|
soil_edit_order = [2, 1, 0]
|
||||||
soil_types = ['clay_composition', 'sand_composition', 'silt_composition']
|
soil_types = ['clay_composition', 'sand_composition', 'silt_composition']
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def load_contents(self):
|
def load_contents(self):
|
||||||
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Soil Texture'})
|
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Soil Texture'})
|
||||||
for doc in docs:
|
for doc in docs:
|
||||||
@ -26,6 +27,7 @@ class SoilTexture(Document):
|
|||||||
if sum(self.get(soil_type) for soil_type in self.soil_types) != 100:
|
if sum(self.get(soil_type) for soil_type in self.soil_types) != 100:
|
||||||
frappe.throw(_('Soil compositions do not add up to 100'))
|
frappe.throw(_('Soil compositions do not add up to 100'))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def update_soil_edit(self, soil_type):
|
def update_soil_edit(self, soil_type):
|
||||||
self.soil_edit_order[self.soil_types.index(soil_type)] = max(self.soil_edit_order)+1
|
self.soil_edit_order[self.soil_types.index(soil_type)] = max(self.soil_edit_order)+1
|
||||||
self.soil_type = self.get_soil_type()
|
self.soil_type = self.get_soil_type()
|
||||||
@ -36,7 +38,7 @@ class SoilTexture(Document):
|
|||||||
last_edit_index = self.soil_edit_order.index(min(self.soil_edit_order))
|
last_edit_index = self.soil_edit_order.index(min(self.soil_edit_order))
|
||||||
|
|
||||||
# set composition of the last edited soil
|
# set composition of the last edited soil
|
||||||
self.set( self.soil_types[last_edit_index],
|
self.set(self.soil_types[last_edit_index],
|
||||||
100 - sum(cint(self.get(soil_type)) for soil_type in self.soil_types) + cint(self.get(self.soil_types[last_edit_index])))
|
100 - sum(cint(self.get(soil_type)) for soil_type in self.soil_types) + cint(self.get(self.soil_types[last_edit_index])))
|
||||||
|
|
||||||
# calculate soil type
|
# calculate soil type
|
||||||
|
|||||||
@ -9,11 +9,13 @@ from frappe.model.document import Document
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
|
|
||||||
class WaterAnalysis(Document):
|
class WaterAnalysis(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def load_contents(self):
|
def load_contents(self):
|
||||||
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Water Analysis'})
|
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Water Analysis'})
|
||||||
for doc in docs:
|
for doc in docs:
|
||||||
self.append('water_analysis_criteria', {'title': str(doc.name)})
|
self.append('water_analysis_criteria', {'title': str(doc.name)})
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def update_lab_result_date(self):
|
def update_lab_result_date(self):
|
||||||
if not self.result_datetime:
|
if not self.result_datetime:
|
||||||
self.result_datetime = self.laboratory_testing_datetime
|
self.result_datetime = self.laboratory_testing_datetime
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import frappe
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class Weather(Document):
|
class Weather(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def load_contents(self):
|
def load_contents(self):
|
||||||
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Weather'})
|
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Weather'})
|
||||||
for doc in docs:
|
for doc in docs:
|
||||||
|
|||||||
@ -553,6 +553,7 @@ class Asset(AccountsController):
|
|||||||
make_gl_entries(gl_entries)
|
make_gl_entries(gl_entries)
|
||||||
self.db_set('booked_fixed_asset', 1)
|
self.db_set('booked_fixed_asset', 1)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_depreciation_rate(self, args, on_validate=False):
|
def get_depreciation_rate(self, args, on_validate=False):
|
||||||
if isinstance(args, string_types):
|
if isinstance(args, string_types):
|
||||||
args = json.loads(args)
|
args = json.loads(args)
|
||||||
|
|||||||
@ -133,6 +133,7 @@ class PurchaseOrder(BuyingController):
|
|||||||
d.material_request_item, "schedule_date")
|
d.material_request_item, "schedule_date")
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_last_purchase_rate(self):
|
def get_last_purchase_rate(self):
|
||||||
"""get last purchase rates for all items"""
|
"""get last purchase rates for all items"""
|
||||||
|
|
||||||
|
|||||||
@ -66,6 +66,7 @@ class RequestforQuotation(BuyingController):
|
|||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
frappe.db.set(self, 'status', 'Cancelled')
|
frappe.db.set(self, 'status', 'Cancelled')
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_supplier_email_preview(self, supplier):
|
def get_supplier_email_preview(self, supplier):
|
||||||
"""Returns formatted email preview as string."""
|
"""Returns formatted email preview as string."""
|
||||||
rfq_suppliers = list(filter(lambda row: row.supplier == supplier, self.suppliers))
|
rfq_suppliers = list(filter(lambda row: row.supplier == supplier, self.suppliers))
|
||||||
|
|||||||
@ -517,6 +517,7 @@ class AccountsController(TransactionBase):
|
|||||||
frappe.db.sql("""delete from `tab%s` where parentfield=%s and parent = %s
|
frappe.db.sql("""delete from `tab%s` where parentfield=%s and parent = %s
|
||||||
and allocated_amount = 0""" % (childtype, '%s', '%s'), (parentfield, self.name))
|
and allocated_amount = 0""" % (childtype, '%s', '%s'), (parentfield, self.name))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def apply_shipping_rule(self):
|
def apply_shipping_rule(self):
|
||||||
if self.shipping_rule:
|
if self.shipping_rule:
|
||||||
shipping_rule = frappe.get_doc("Shipping Rule", self.shipping_rule)
|
shipping_rule = frappe.get_doc("Shipping Rule", self.shipping_rule)
|
||||||
@ -537,6 +538,7 @@ class AccountsController(TransactionBase):
|
|||||||
|
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def set_advances(self):
|
def set_advances(self):
|
||||||
"""Returns list of advances against Account, Party, Reference"""
|
"""Returns list of advances against Account, Party, Reference"""
|
||||||
|
|
||||||
@ -921,7 +923,8 @@ class AccountsController(TransactionBase):
|
|||||||
else:
|
else:
|
||||||
for d in self.get("payment_schedule"):
|
for d in self.get("payment_schedule"):
|
||||||
if d.invoice_portion:
|
if d.invoice_portion:
|
||||||
d.payment_amount = flt(grand_total * flt(d.invoice_portion) / 100, d.precision('payment_amount'))
|
d.payment_amount = flt(grand_total * flt(d.invoice_portion / 100), d.precision('payment_amount'))
|
||||||
|
d.outstanding = d.payment_amount
|
||||||
|
|
||||||
def set_due_date(self):
|
def set_due_date(self):
|
||||||
due_dates = [d.due_date for d in self.get("payment_schedule") if d.due_date]
|
due_dates = [d.due_date for d in self.get("payment_schedule") if d.due_date]
|
||||||
@ -1236,18 +1239,24 @@ def get_payment_term_details(term, posting_date=None, grand_total=None, bill_dat
|
|||||||
term_details.description = term.description
|
term_details.description = term.description
|
||||||
term_details.invoice_portion = term.invoice_portion
|
term_details.invoice_portion = term.invoice_portion
|
||||||
term_details.payment_amount = flt(term.invoice_portion) * flt(grand_total) / 100
|
term_details.payment_amount = flt(term.invoice_portion) * flt(grand_total) / 100
|
||||||
|
term_details.discount_type = term.discount_type
|
||||||
|
term_details.discount = term.discount
|
||||||
|
# term_details.discounted_amount = flt(grand_total) * (term.discount / 100) if term.discount_type == 'Percentage' else discount
|
||||||
|
term_details.outstanding = term_details.payment_amount
|
||||||
|
term_details.mode_of_payment = term.mode_of_payment
|
||||||
|
|
||||||
if bill_date:
|
if bill_date:
|
||||||
term_details.due_date = get_due_date(term, bill_date)
|
term_details.due_date = get_due_date(term, bill_date)
|
||||||
|
term_details.discount_date = get_discount_date(term, bill_date)
|
||||||
elif posting_date:
|
elif posting_date:
|
||||||
term_details.due_date = get_due_date(term, posting_date)
|
term_details.due_date = get_due_date(term, posting_date)
|
||||||
|
term_details.discount_date = get_discount_date(term, posting_date)
|
||||||
|
|
||||||
if getdate(term_details.due_date) < getdate(posting_date):
|
if getdate(term_details.due_date) < getdate(posting_date):
|
||||||
term_details.due_date = posting_date
|
term_details.due_date = posting_date
|
||||||
term_details.mode_of_payment = term.mode_of_payment
|
|
||||||
|
|
||||||
return term_details
|
return term_details
|
||||||
|
|
||||||
|
|
||||||
def get_due_date(term, posting_date=None, bill_date=None):
|
def get_due_date(term, posting_date=None, bill_date=None):
|
||||||
due_date = None
|
due_date = None
|
||||||
date = bill_date or posting_date
|
date = bill_date or posting_date
|
||||||
@ -1259,6 +1268,16 @@ def get_due_date(term, posting_date=None, bill_date=None):
|
|||||||
due_date = add_months(get_last_day(date), term.credit_months)
|
due_date = add_months(get_last_day(date), term.credit_months)
|
||||||
return due_date
|
return due_date
|
||||||
|
|
||||||
|
def get_discount_date(term, posting_date=None, bill_date=None):
|
||||||
|
discount_validity = None
|
||||||
|
date = bill_date or posting_date
|
||||||
|
if term.discount_validity_based_on == "Day(s) after invoice date":
|
||||||
|
discount_validity = add_days(date, term.discount_validity)
|
||||||
|
elif term.discount_validity_based_on == "Day(s) after the end of the invoice month":
|
||||||
|
discount_validity = add_days(get_last_day(date), term.discount_validity)
|
||||||
|
elif term.discount_validity_based_on == "Month(s) after the end of the invoice month":
|
||||||
|
discount_validity = add_months(get_last_day(date), term.discount_validity)
|
||||||
|
return discount_validity
|
||||||
|
|
||||||
def get_supplier_block_status(party_name):
|
def get_supplier_block_status(party_name):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -11,6 +11,7 @@ from frappe.utils.file_manager import get_file, get_file_path
|
|||||||
from six.moves.urllib.parse import urlencode
|
from six.moves.urllib.parse import urlencode
|
||||||
|
|
||||||
class LinkedInSettings(Document):
|
class LinkedInSettings(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def get_authorization_url(self):
|
def get_authorization_url(self):
|
||||||
params = urlencode({
|
params = urlencode({
|
||||||
"response_type":"code",
|
"response_type":"code",
|
||||||
|
|||||||
@ -85,6 +85,7 @@ class Opportunity(TransactionBase):
|
|||||||
self.opportunity_from = "Lead"
|
self.opportunity_from = "Lead"
|
||||||
self.party_name = lead_name
|
self.party_name = lead_name
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def declare_enquiry_lost(self, lost_reasons_list, detailed_reason=None):
|
def declare_enquiry_lost(self, lost_reasons_list, detailed_reason=None):
|
||||||
if not self.has_active_quotation():
|
if not self.has_active_quotation():
|
||||||
frappe.db.set(self, 'status', 'Lost')
|
frappe.db.set(self, 'status', 'Lost')
|
||||||
|
|||||||
@ -11,6 +11,7 @@ from frappe.utils import get_url_to_form, get_link_to_form
|
|||||||
from tweepy.error import TweepError
|
from tweepy.error import TweepError
|
||||||
|
|
||||||
class TwitterSettings(Document):
|
class TwitterSettings(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def get_authorize_url(self):
|
def get_authorize_url(self):
|
||||||
callback_url = "{0}/api/method/erpnext.crm.doctype.twitter_settings.twitter_settings.callback?".format(frappe.utils.get_url())
|
callback_url = "{0}/api/method/erpnext.crm.doctype.twitter_settings.twitter_settings.callback?".format(frappe.utils.get_url())
|
||||||
auth = tweepy.OAuthHandler(self.consumer_key, self.get_password(fieldname="consumer_secret"), callback_url)
|
auth = tweepy.OAuthHandler(self.consumer_key, self.get_password(fieldname="consumer_secret"), callback_url)
|
||||||
|
|||||||
@ -13,6 +13,7 @@ from erpnext.education.utils import OverlapError
|
|||||||
|
|
||||||
class CourseSchedulingTool(Document):
|
class CourseSchedulingTool(Document):
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def schedule_course(self):
|
def schedule_course(self):
|
||||||
"""Creates course schedules as per specified parameters"""
|
"""Creates course schedules as per specified parameters"""
|
||||||
|
|
||||||
|
|||||||
@ -52,6 +52,7 @@ class FeeSchedule(Document):
|
|||||||
self.grand_total = no_of_students*self.total_amount
|
self.grand_total = no_of_students*self.total_amount
|
||||||
self.grand_total_in_words = money_in_words(self.grand_total)
|
self.grand_total_in_words = money_in_words(self.grand_total)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def create_fees(self):
|
def create_fees(self):
|
||||||
self.db_set("fee_creation_status", "In Process")
|
self.db_set("fee_creation_status", "In Process")
|
||||||
frappe.publish_realtime("fee_schedule_progress",
|
frappe.publish_realtime("fee_schedule_progress",
|
||||||
|
|||||||
@ -91,6 +91,8 @@ class ProgramEnrollment(Document):
|
|||||||
(fee, fee) for fee in fee_list]
|
(fee, fee) for fee in fee_list]
|
||||||
msgprint(_("Fee Records Created - {0}").format(comma_and(fee_list)))
|
msgprint(_("Fee Records Created - {0}").format(comma_and(fee_list)))
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_courses(self):
|
def get_courses(self):
|
||||||
return frappe.db.sql('''select course from `tabProgram Course` where parent = %s and required = 1''', (self.program), as_dict=1)
|
return frappe.db.sql('''select course from `tabProgram Course` where parent = %s and required = 1''', (self.program), as_dict=1)
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,7 @@ class ProgramEnrollmentTool(Document):
|
|||||||
academic_term_reqd = cint(frappe.db.get_single_value('Education Settings', 'academic_term_reqd'))
|
academic_term_reqd = cint(frappe.db.get_single_value('Education Settings', 'academic_term_reqd'))
|
||||||
self.set_onload("academic_term_reqd", academic_term_reqd)
|
self.set_onload("academic_term_reqd", academic_term_reqd)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_students(self):
|
def get_students(self):
|
||||||
students = []
|
students = []
|
||||||
if not self.get_students_from:
|
if not self.get_students_from:
|
||||||
@ -49,6 +50,7 @@ class ProgramEnrollmentTool(Document):
|
|||||||
else:
|
else:
|
||||||
frappe.throw(_("No students Found"))
|
frappe.throw(_("No students Found"))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def enroll_students(self):
|
def enroll_students(self):
|
||||||
total = len(self.students)
|
total = len(self.students)
|
||||||
for i, stud in enumerate(self.students):
|
for i, stud in enumerate(self.students):
|
||||||
|
|||||||
@ -9,6 +9,7 @@ from frappe.model.document import Document
|
|||||||
from erpnext.education.doctype.student_group.student_group import get_students
|
from erpnext.education.doctype.student_group.student_group import get_students
|
||||||
|
|
||||||
class StudentGroupCreationTool(Document):
|
class StudentGroupCreationTool(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def get_courses(self):
|
def get_courses(self):
|
||||||
group_list = []
|
group_list = []
|
||||||
|
|
||||||
@ -42,6 +43,7 @@ class StudentGroupCreationTool(Document):
|
|||||||
|
|
||||||
return group_list
|
return group_list
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def create_student_groups(self):
|
def create_student_groups(self):
|
||||||
if not self.courses:
|
if not self.courses:
|
||||||
frappe.throw(_("""No Student Groups created."""))
|
frappe.throw(_("""No Student Groups created."""))
|
||||||
|
|||||||
@ -62,6 +62,7 @@ class MpesaSettings(Document):
|
|||||||
|
|
||||||
return request_amounts
|
return request_amounts
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_account_balance_info(self):
|
def get_account_balance_info(self):
|
||||||
payload = dict(
|
payload = dict(
|
||||||
reference_doctype="Mpesa Settings",
|
reference_doctype="Mpesa Settings",
|
||||||
|
|||||||
@ -15,6 +15,7 @@ from frappe.utils import add_months, formatdate, getdate, today
|
|||||||
|
|
||||||
class PlaidSettings(Document):
|
class PlaidSettings(Document):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@frappe.whitelist()
|
||||||
def get_link_token():
|
def get_link_token():
|
||||||
plaid = PlaidConnector()
|
plaid = PlaidConnector()
|
||||||
return plaid.get_link_token()
|
return plaid.get_link_token()
|
||||||
|
|||||||
@ -54,6 +54,7 @@ class QuickBooksMigrator(Document):
|
|||||||
self.authorization_url = self.oauth.authorization_url(self.authorization_endpoint)[0]
|
self.authorization_url = self.oauth.authorization_url(self.authorization_endpoint)[0]
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def migrate(self):
|
def migrate(self):
|
||||||
frappe.enqueue_doc("QuickBooks Migrator", "QuickBooks Migrator", "_migrate", queue="long")
|
frappe.enqueue_doc("QuickBooks Migrator", "QuickBooks Migrator", "_migrate", queue="long")
|
||||||
|
|
||||||
|
|||||||
@ -594,18 +594,22 @@ class TallyMigration(Document):
|
|||||||
frappe.db.set_value("Price List", "Tally Price List", "enabled", 0)
|
frappe.db.set_value("Price List", "Tally Price List", "enabled", 0)
|
||||||
frappe.flags.in_migrate = False
|
frappe.flags.in_migrate = False
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def process_master_data(self):
|
def process_master_data(self):
|
||||||
self.set_status("Processing Master Data")
|
self.set_status("Processing Master Data")
|
||||||
frappe.enqueue_doc(self.doctype, self.name, "_process_master_data", queue="long", timeout=3600)
|
frappe.enqueue_doc(self.doctype, self.name, "_process_master_data", queue="long", timeout=3600)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def import_master_data(self):
|
def import_master_data(self):
|
||||||
self.set_status("Importing Master Data")
|
self.set_status("Importing Master Data")
|
||||||
frappe.enqueue_doc(self.doctype, self.name, "_import_master_data", queue="long", timeout=3600)
|
frappe.enqueue_doc(self.doctype, self.name, "_import_master_data", queue="long", timeout=3600)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def process_day_book_data(self):
|
def process_day_book_data(self):
|
||||||
self.set_status("Processing Day Book Data")
|
self.set_status("Processing Day Book Data")
|
||||||
frappe.enqueue_doc(self.doctype, self.name, "_process_day_book_data", queue="long", timeout=3600)
|
frappe.enqueue_doc(self.doctype, self.name, "_process_day_book_data", queue="long", timeout=3600)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def import_day_book_data(self):
|
def import_day_book_data(self):
|
||||||
self.set_status("Importing Day Book Data")
|
self.set_status("Importing Day Book Data")
|
||||||
frappe.enqueue_doc(self.doctype, self.name, "_import_day_book_data", queue="long", timeout=3600)
|
frappe.enqueue_doc(self.doctype, self.name, "_import_day_book_data", queue="long", timeout=3600)
|
||||||
|
|||||||
@ -54,6 +54,7 @@ class ClinicalProcedure(Document):
|
|||||||
def set_title(self):
|
def set_title(self):
|
||||||
self.title = _('{0} - {1}').format(self.patient_name or self.patient, self.procedure_template)[:100]
|
self.title = _('{0} - {1}').format(self.patient_name or self.patient, self.procedure_template)[:100]
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def complete_procedure(self):
|
def complete_procedure(self):
|
||||||
if self.consume_stock and self.items:
|
if self.consume_stock and self.items:
|
||||||
stock_entry = make_stock_entry(self)
|
stock_entry = make_stock_entry(self)
|
||||||
@ -96,6 +97,7 @@ class ClinicalProcedure(Document):
|
|||||||
if self.consume_stock and self.items:
|
if self.consume_stock and self.items:
|
||||||
return stock_entry
|
return stock_entry
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def start_procedure(self):
|
def start_procedure(self):
|
||||||
allow_start = self.set_actual_qty()
|
allow_start = self.set_actual_qty()
|
||||||
if allow_start:
|
if allow_start:
|
||||||
@ -116,6 +118,7 @@ class ClinicalProcedure(Document):
|
|||||||
|
|
||||||
return allow_start
|
return allow_start
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def make_material_receipt(self, submit=False):
|
def make_material_receipt(self, submit=False):
|
||||||
stock_entry = frappe.new_doc('Stock Entry')
|
stock_entry = frappe.new_doc('Stock Entry')
|
||||||
|
|
||||||
|
|||||||
@ -14,6 +14,7 @@ class InpatientMedicationEntry(Document):
|
|||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_medication_orders()
|
self.validate_medication_orders()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_medication_orders(self):
|
def get_medication_orders(self):
|
||||||
# pull inpatient medication orders based on selected filters
|
# pull inpatient medication orders based on selected filters
|
||||||
orders = get_pending_medication_orders(self)
|
orders = get_pending_medication_orders(self)
|
||||||
|
|||||||
@ -57,6 +57,7 @@ class InpatientMedicationOrder(Document):
|
|||||||
|
|
||||||
self.db_set('status', status)
|
self.db_set('status', status)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def add_order_entries(self, order):
|
def add_order_entries(self, order):
|
||||||
if order.get('drug_code'):
|
if order.get('drug_code'):
|
||||||
dosage = frappe.get_doc('Prescription Dosage', order.get('dosage'))
|
dosage = frappe.get_doc('Prescription Dosage', order.get('dosage'))
|
||||||
|
|||||||
@ -53,12 +53,15 @@ class InpatientRecord(Document):
|
|||||||
+ """ <b><a href="/app/Form/Inpatient Record/{0}">{0}</a></b>""".format(ip_record[0].name))
|
+ """ <b><a href="/app/Form/Inpatient Record/{0}">{0}</a></b>""".format(ip_record[0].name))
|
||||||
frappe.throw(msg)
|
frappe.throw(msg)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def admit(self, service_unit, check_in, expected_discharge=None):
|
def admit(self, service_unit, check_in, expected_discharge=None):
|
||||||
admit_patient(self, service_unit, check_in, expected_discharge)
|
admit_patient(self, service_unit, check_in, expected_discharge)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def discharge(self):
|
def discharge(self):
|
||||||
discharge_patient(self)
|
discharge_patient(self)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def transfer(self, service_unit, check_in, leave_from):
|
def transfer(self, service_unit, check_in, leave_from):
|
||||||
if leave_from:
|
if leave_from:
|
||||||
patient_leave_service_unit(self, check_in, leave_from)
|
patient_leave_service_unit(self, check_in, leave_from)
|
||||||
|
|||||||
@ -111,6 +111,7 @@ class Patient(Document):
|
|||||||
age_str = str(age.years) + ' ' + _("Years(s)") + ' ' + str(age.months) + ' ' + _("Month(s)") + ' ' + str(age.days) + ' ' + _("Day(s)")
|
age_str = str(age.years) + ' ' + _("Years(s)") + ' ' + str(age.months) + ' ' + _("Month(s)") + ' ' + str(age.days) + ' ' + _("Day(s)")
|
||||||
return age_str
|
return age_str
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def invoice_patient_registration(self):
|
def invoice_patient_registration(self):
|
||||||
if frappe.db.get_single_value('Healthcare Settings', 'registration_fee'):
|
if frappe.db.get_single_value('Healthcare Settings', 'registration_fee'):
|
||||||
company = frappe.defaults.get_user_default('company')
|
company = frappe.defaults.get_user_default('company')
|
||||||
|
|||||||
@ -113,6 +113,7 @@ class PatientAppointment(Document):
|
|||||||
if fee_validity:
|
if fee_validity:
|
||||||
frappe.msgprint(_('{0} has fee validity till {1}').format(self.patient, fee_validity.valid_till))
|
frappe.msgprint(_('{0} has fee validity till {1}').format(self.patient, fee_validity.valid_till))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_therapy_types(self):
|
def get_therapy_types(self):
|
||||||
if not self.therapy_plan:
|
if not self.therapy_plan:
|
||||||
return
|
return
|
||||||
|
|||||||
@ -34,6 +34,7 @@ class PatientHistorySettings(Document):
|
|||||||
frappe.throw(_('Row #{0}: Field {1} in Document Type {2} is not a Date / Datetime field.').format(
|
frappe.throw(_('Row #{0}: Field {1} in Document Type {2} is not a Date / Datetime field.').format(
|
||||||
entry.idx, frappe.bold(entry.date_fieldname), frappe.bold(entry.document_type)))
|
entry.idx, frappe.bold(entry.date_fieldname), frappe.bold(entry.document_type)))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_doctype_fields(self, document_type, fields):
|
def get_doctype_fields(self, document_type, fields):
|
||||||
multicheck_fields = []
|
multicheck_fields = []
|
||||||
doc_fields = frappe.get_meta(document_type).fields
|
doc_fields = frappe.get_meta(document_type).fields
|
||||||
@ -49,6 +50,7 @@ class PatientHistorySettings(Document):
|
|||||||
|
|
||||||
return multicheck_fields
|
return multicheck_fields
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_date_field_for_dt(self, document_type):
|
def get_date_field_for_dt(self, document_type):
|
||||||
meta = frappe.get_meta(document_type)
|
meta = frappe.get_meta(document_type)
|
||||||
date_fields = meta.get('fields', {
|
date_fields = meta.get('fields', {
|
||||||
|
|||||||
@ -33,6 +33,7 @@ class TherapyPlan(Document):
|
|||||||
self.db_set('total_sessions', total_sessions)
|
self.db_set('total_sessions', total_sessions)
|
||||||
self.db_set('total_sessions_completed', total_sessions_completed)
|
self.db_set('total_sessions_completed', total_sessions_completed)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def set_therapy_details_from_template(self):
|
def set_therapy_details_from_template(self):
|
||||||
# Add therapy types in the child table
|
# Add therapy types in the child table
|
||||||
self.set('therapy_plan_details', [])
|
self.set('therapy_plan_details', [])
|
||||||
|
|||||||
@ -211,6 +211,7 @@ class ExpenseClaim(AccountsController):
|
|||||||
self.total_claimed_amount += flt(d.amount)
|
self.total_claimed_amount += flt(d.amount)
|
||||||
self.total_sanctioned_amount += flt(d.sanctioned_amount)
|
self.total_sanctioned_amount += flt(d.sanctioned_amount)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def calculate_taxes(self):
|
def calculate_taxes(self):
|
||||||
self.total_taxes_and_charges = 0
|
self.total_taxes_and_charges = 0
|
||||||
for tax in self.taxes:
|
for tax in self.taxes:
|
||||||
|
|||||||
@ -99,6 +99,7 @@ class LeaveAllocation(Document):
|
|||||||
.format(formatdate(future_allocation[0].from_date), future_allocation[0].name),
|
.format(formatdate(future_allocation[0].from_date), future_allocation[0].name),
|
||||||
BackDatedAllocationError)
|
BackDatedAllocationError)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def set_total_leaves_allocated(self):
|
def set_total_leaves_allocated(self):
|
||||||
self.unused_leaves = get_carry_forwarded_leaves(self.employee,
|
self.unused_leaves = get_carry_forwarded_leaves(self.employee,
|
||||||
self.leave_type, self.from_date, self.carry_forward)
|
self.leave_type, self.from_date, self.carry_forward)
|
||||||
|
|||||||
@ -63,6 +63,7 @@ class LeaveEncashment(Document):
|
|||||||
frappe.db.get_value('Leave Allocation', self.leave_allocation, 'total_leaves_encashed') - self.encashable_days)
|
frappe.db.get_value('Leave Allocation', self.leave_allocation, 'total_leaves_encashed') - self.encashable_days)
|
||||||
self.create_leave_ledger_entry(submit=False)
|
self.create_leave_ledger_entry(submit=False)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_leave_details_for_encashment(self):
|
def get_leave_details_for_encashment(self):
|
||||||
salary_structure = get_assigned_salary_structure(self.employee, self.encashment_date or getdate(nowdate()))
|
salary_structure = get_assigned_salary_structure(self.employee, self.encashment_date or getdate(nowdate()))
|
||||||
if not salary_structure:
|
if not salary_structure:
|
||||||
|
|||||||
@ -36,6 +36,7 @@ class LeavePolicyAssignment(Document):
|
|||||||
frappe.throw(_("Leave Policy: {0} already assigned for Employee {1} for period {2} to {3}")
|
frappe.throw(_("Leave Policy: {0} already assigned for Employee {1} for period {2} to {3}")
|
||||||
.format(bold(self.leave_policy), bold(self.employee), bold(formatdate(self.effective_from)), bold(formatdate(self.effective_to))))
|
.format(bold(self.leave_policy), bold(self.employee), bold(formatdate(self.effective_from)), bold(formatdate(self.effective_to))))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def grant_leave_alloc_for_employee(self):
|
def grant_leave_alloc_for_employee(self):
|
||||||
if self.leaves_allocated:
|
if self.leaves_allocated:
|
||||||
frappe.throw(_("Leave already have been assigned for this Leave Policy Assignment"))
|
frappe.throw(_("Leave already have been assigned for this Leave Policy Assignment"))
|
||||||
|
|||||||
@ -15,6 +15,7 @@ from erpnext.hr.doctype.attendance.attendance import mark_attendance
|
|||||||
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
||||||
|
|
||||||
class ShiftType(Document):
|
class ShiftType(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def process_auto_attendance(self):
|
def process_auto_attendance(self):
|
||||||
if not cint(self.enable_auto_attendance) or not self.process_attendance_after or not self.last_sync_of_checkin:
|
if not cint(self.enable_auto_attendance) or not self.process_attendance_after or not self.last_sync_of_checkin:
|
||||||
return
|
return
|
||||||
|
|||||||
@ -31,7 +31,7 @@ def get_columns():
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"fieldname": "job_opening",
|
"fieldname": "job_opening",
|
||||||
"options": "Job Opening",
|
"options": "Job Opening",
|
||||||
"width": 100
|
"width": 105
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": _("Job Applicant"),
|
"label": _("Job Applicant"),
|
||||||
@ -44,13 +44,13 @@ def get_columns():
|
|||||||
"label": _("Applicant name"),
|
"label": _("Applicant name"),
|
||||||
"fieldtype": "data",
|
"fieldtype": "data",
|
||||||
"fieldname": "applicant_name",
|
"fieldname": "applicant_name",
|
||||||
"width": 120
|
"width": 130
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": _("Application Status"),
|
"label": _("Application Status"),
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"fieldname": "application_status",
|
"fieldname": "application_status",
|
||||||
"width": 100
|
"width": 150
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": _("Job Offer"),
|
"label": _("Job Offer"),
|
||||||
|
|||||||
@ -12,6 +12,7 @@ from erpnext.stock.utils import get_valid_serial_nos
|
|||||||
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
||||||
|
|
||||||
class MaintenanceSchedule(TransactionBase):
|
class MaintenanceSchedule(TransactionBase):
|
||||||
|
@frappe.whitelist()
|
||||||
def generate_schedule(self):
|
def generate_schedule(self):
|
||||||
self.set('schedules', [])
|
self.set('schedules', [])
|
||||||
frappe.db.sql("""delete from `tabMaintenance Schedule Detail`
|
frappe.db.sql("""delete from `tabMaintenance Schedule Detail`
|
||||||
|
|||||||
@ -113,6 +113,7 @@ class BOM(WebsiteGenerator):
|
|||||||
|
|
||||||
return item
|
return item
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_routing(self):
|
def get_routing(self):
|
||||||
if self.routing:
|
if self.routing:
|
||||||
self.set("operations", [])
|
self.set("operations", [])
|
||||||
@ -145,6 +146,7 @@ class BOM(WebsiteGenerator):
|
|||||||
if not item.get(r):
|
if not item.get(r):
|
||||||
item.set(r, ret[r])
|
item.set(r, ret[r])
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_bom_material_detail(self, args=None):
|
def get_bom_material_detail(self, args=None):
|
||||||
""" Get raw material details like uom, desc and rate"""
|
""" Get raw material details like uom, desc and rate"""
|
||||||
if not args:
|
if not args:
|
||||||
@ -210,6 +212,7 @@ class BOM(WebsiteGenerator):
|
|||||||
.format(self.rm_cost_as_per, arg["item_code"]), alert=True)
|
.format(self.rm_cost_as_per, arg["item_code"]), alert=True)
|
||||||
return flt(rate) * flt(self.plc_conversion_rate or 1) / (self.conversion_rate or 1)
|
return flt(rate) * flt(self.plc_conversion_rate or 1) / (self.conversion_rate or 1)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def update_cost(self, update_parent=True, from_child_bom=False, save=True):
|
def update_cost(self, update_parent=True, from_child_bom=False, save=True):
|
||||||
if self.docstatus == 2:
|
if self.docstatus == 2:
|
||||||
return
|
return
|
||||||
|
|||||||
@ -164,6 +164,7 @@ class JobCard(Document):
|
|||||||
"time_in_mins": time_diff_in_minutes(row.planned_end_time, row.planned_start_time),
|
"time_in_mins": time_diff_in_minutes(row.planned_end_time, row.planned_start_time),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_required_items(self):
|
def get_required_items(self):
|
||||||
if not self.get('work_order'):
|
if not self.get('work_order'):
|
||||||
return
|
return
|
||||||
|
|||||||
@ -29,6 +29,7 @@ class ProductionPlan(Document):
|
|||||||
if not flt(d.planned_qty):
|
if not flt(d.planned_qty):
|
||||||
frappe.throw(_("Please enter Planned Qty for Item {0} at row {1}").format(d.item_code, d.idx))
|
frappe.throw(_("Please enter Planned Qty for Item {0} at row {1}").format(d.item_code, d.idx))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_open_sales_orders(self):
|
def get_open_sales_orders(self):
|
||||||
""" Pull sales orders which are pending to deliver based on criteria selected"""
|
""" Pull sales orders which are pending to deliver based on criteria selected"""
|
||||||
open_so = get_sales_orders(self)
|
open_so = get_sales_orders(self)
|
||||||
@ -50,6 +51,7 @@ class ProductionPlan(Document):
|
|||||||
'grand_total': data.base_grand_total
|
'grand_total': data.base_grand_total
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_pending_material_requests(self):
|
def get_pending_material_requests(self):
|
||||||
""" Pull Material Requests that are pending based on criteria selected"""
|
""" Pull Material Requests that are pending based on criteria selected"""
|
||||||
mr_filter = item_filter = ""
|
mr_filter = item_filter = ""
|
||||||
@ -92,6 +94,7 @@ class ProductionPlan(Document):
|
|||||||
'material_request_date': data.transaction_date
|
'material_request_date': data.transaction_date
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_items(self):
|
def get_items(self):
|
||||||
if self.get_items_from == "Sales Order":
|
if self.get_items_from == "Sales Order":
|
||||||
self.get_so_items()
|
self.get_so_items()
|
||||||
@ -219,6 +222,7 @@ class ProductionPlan(Document):
|
|||||||
filters = {'docstatus': 0, 'production_plan': ("=", self.name)}):
|
filters = {'docstatus': 0, 'production_plan': ("=", self.name)}):
|
||||||
frappe.delete_doc('Work Order', d.name)
|
frappe.delete_doc('Work Order', d.name)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def set_status(self, close=None):
|
def set_status(self, close=None):
|
||||||
self.status = {
|
self.status = {
|
||||||
0: 'Draft',
|
0: 'Draft',
|
||||||
@ -302,6 +306,7 @@ class ProductionPlan(Document):
|
|||||||
|
|
||||||
return item_dict
|
return item_dict
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def make_work_order(self):
|
def make_work_order(self):
|
||||||
wo_list = []
|
wo_list = []
|
||||||
self.validate_data()
|
self.validate_data()
|
||||||
@ -367,6 +372,7 @@ class ProductionPlan(Document):
|
|||||||
except OverProductionError:
|
except OverProductionError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def make_material_request(self):
|
def make_material_request(self):
|
||||||
'''Create Material Requests grouped by Sales Order and Material Request Type'''
|
'''Create Material Requests grouped by Sales Order and Material Request Type'''
|
||||||
material_request_list = []
|
material_request_list = []
|
||||||
|
|||||||
@ -74,7 +74,7 @@ def setup_bom(**args):
|
|||||||
})
|
})
|
||||||
|
|
||||||
if not args.raw_materials:
|
if not args.raw_materials:
|
||||||
if not frappe.db.exists('Item', "Test Extra Item 1"):
|
if not frappe.db.exists('Item', "Test Extra Item N-1"):
|
||||||
make_item("Test Extra Item N-1", {
|
make_item("Test Extra Item N-1", {
|
||||||
'is_stock_item': 1,
|
'is_stock_item': 1,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -509,6 +509,7 @@ class WorkOrder(Document):
|
|||||||
stock_bin = get_bin(d.item_code, d.source_warehouse)
|
stock_bin = get_bin(d.item_code, d.source_warehouse)
|
||||||
stock_bin.update_reserved_qty_for_production()
|
stock_bin.update_reserved_qty_for_production()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_items_and_operations_from_bom(self):
|
def get_items_and_operations_from_bom(self):
|
||||||
self.set_required_items()
|
self.set_required_items()
|
||||||
self.set_work_order_operations()
|
self.set_work_order_operations()
|
||||||
@ -613,6 +614,7 @@ class WorkOrder(Document):
|
|||||||
|
|
||||||
item.db_set('consumed_qty', flt(consumed_qty), update_modified=False)
|
item.db_set('consumed_qty', flt(consumed_qty), update_modified=False)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def make_bom(self):
|
def make_bom(self):
|
||||||
data = frappe.db.sql(""" select sed.item_code, sed.qty, sed.s_warehouse
|
data = frappe.db.sql(""" select sed.item_code, sed.qty, sed.s_warehouse
|
||||||
from `tabStock Entry Detail` sed, `tabStock Entry` se
|
from `tabStock Entry Detail` sed, `tabStock Entry` se
|
||||||
|
|||||||
@ -53,6 +53,7 @@ class Member(Document):
|
|||||||
|
|
||||||
return subscription
|
return subscription
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def make_customer_and_link(self):
|
def make_customer_and_link(self):
|
||||||
if self.customer:
|
if self.customer:
|
||||||
frappe.msgprint(_("A customer is already linked to this Member"))
|
frappe.msgprint(_("A customer is already linked to this Member"))
|
||||||
|
|||||||
@ -74,6 +74,7 @@ class Membership(Document):
|
|||||||
self.generate_invoice(with_payment_entry=settings.automate_membership_payment_entries, save=True)
|
self.generate_invoice(with_payment_entry=settings.automate_membership_payment_entries, save=True)
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def generate_invoice(self, save=True, with_payment_entry=False):
|
def generate_invoice(self, save=True, with_payment_entry=False):
|
||||||
if not (self.paid or self.currency or self.amount):
|
if not (self.paid or self.currency or self.amount):
|
||||||
frappe.throw(_("The payment for this membership is not paid. To generate invoice fill the payment details"))
|
frappe.throw(_("The payment for this membership is not paid. To generate invoice fill the payment details"))
|
||||||
@ -130,6 +131,7 @@ class Membership(Document):
|
|||||||
pe.save()
|
pe.save()
|
||||||
pe.submit()
|
pe.submit()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def send_acknowlement(self):
|
def send_acknowlement(self):
|
||||||
settings = frappe.get_doc("Non Profit Settings")
|
settings = frappe.get_doc("Non Profit Settings")
|
||||||
if not settings.send_email:
|
if not settings.send_email:
|
||||||
|
|||||||
@ -9,6 +9,7 @@ from frappe.integrations.utils import get_payment_gateway_controller
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class NonProfitSettings(Document):
|
class NonProfitSettings(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def generate_webhook_secret(self, field="membership_webhook_secret"):
|
def generate_webhook_secret(self, field="membership_webhook_secret"):
|
||||||
key = frappe.generate_hash(length=20)
|
key = frappe.generate_hash(length=20)
|
||||||
self.set(field, key)
|
self.set(field, key)
|
||||||
@ -21,6 +22,7 @@ class NonProfitSettings(Document):
|
|||||||
_("Webhook Secret")
|
_("Webhook Secret")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def revoke_key(self, key):
|
def revoke_key(self, key):
|
||||||
self.set(key, None)
|
self.set(key, None)
|
||||||
self.save()
|
self.save()
|
||||||
|
|||||||
@ -752,6 +752,7 @@ erpnext.patches.v13_0.set_company_in_leave_ledger_entry
|
|||||||
erpnext.patches.v13_0.convert_qi_parameter_to_link_field
|
erpnext.patches.v13_0.convert_qi_parameter_to_link_field
|
||||||
erpnext.patches.v13_0.setup_patient_history_settings_for_standard_doctypes
|
erpnext.patches.v13_0.setup_patient_history_settings_for_standard_doctypes
|
||||||
erpnext.patches.v13_0.add_naming_series_to_old_projects # 1-02-2021
|
erpnext.patches.v13_0.add_naming_series_to_old_projects # 1-02-2021
|
||||||
|
erpnext.patches.v13_0.update_payment_terms_outstanding
|
||||||
erpnext.patches.v12_0.add_state_code_for_ladakh
|
erpnext.patches.v12_0.add_state_code_for_ladakh
|
||||||
erpnext.patches.v13_0.item_reposting_for_incorrect_sl_and_gl
|
erpnext.patches.v13_0.item_reposting_for_incorrect_sl_and_gl
|
||||||
erpnext.patches.v13_0.delete_old_bank_reconciliation_doctypes
|
erpnext.patches.v13_0.delete_old_bank_reconciliation_doctypes
|
||||||
|
|||||||
15
erpnext/patches/v13_0/update_payment_terms_outstanding.py
Normal file
15
erpnext/patches/v13_0/update_payment_terms_outstanding.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# MIT License. See license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
frappe.reload_doc("accounts", "doctype", "Payment Schedule")
|
||||||
|
if frappe.db.count('Payment Schedule'):
|
||||||
|
frappe.db.sql('''
|
||||||
|
UPDATE
|
||||||
|
`tabPayment Schedule` ps
|
||||||
|
SET
|
||||||
|
ps.outstanding = (ps.payment_amount - ps.paid_amount)
|
||||||
|
''')
|
||||||
@ -95,6 +95,7 @@ class PayrollEntry(Document):
|
|||||||
|
|
||||||
return emp_list
|
return emp_list
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def fill_employee_details(self):
|
def fill_employee_details(self):
|
||||||
self.set('employees', [])
|
self.set('employees', [])
|
||||||
employees = self.get_emp_list()
|
employees = self.get_emp_list()
|
||||||
@ -142,6 +143,7 @@ class PayrollEntry(Document):
|
|||||||
if not self.get(fieldname):
|
if not self.get(fieldname):
|
||||||
frappe.throw(_("Please set {0}").format(self.meta.get_label(fieldname)))
|
frappe.throw(_("Please set {0}").format(self.meta.get_label(fieldname)))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def create_salary_slips(self):
|
def create_salary_slips(self):
|
||||||
"""
|
"""
|
||||||
Creates salary slip for selected employees if already not created
|
Creates salary slip for selected employees if already not created
|
||||||
@ -329,6 +331,7 @@ class PayrollEntry(Document):
|
|||||||
amount = flt(amount) * flt(conversion_rate)
|
amount = flt(amount) * flt(conversion_rate)
|
||||||
return exchange_rate, amount
|
return exchange_rate, amount
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def make_payment_entry(self):
|
def make_payment_entry(self):
|
||||||
self.check_permission('write')
|
self.check_permission('write')
|
||||||
|
|
||||||
|
|||||||
@ -142,6 +142,7 @@ class SalarySlip(TransactionBase):
|
|||||||
self.start_date = date_details.start_date
|
self.start_date = date_details.start_date
|
||||||
self.end_date = date_details.end_date
|
self.end_date = date_details.end_date
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_emp_and_working_day_details(self):
|
def get_emp_and_working_day_details(self):
|
||||||
'''First time, load all the components from salary structure'''
|
'''First time, load all the components from salary structure'''
|
||||||
if self.employee:
|
if self.employee:
|
||||||
@ -1114,10 +1115,12 @@ class SalarySlip(TransactionBase):
|
|||||||
self.bank_name = emp.bank_name
|
self.bank_name = emp.bank_name
|
||||||
self.bank_account_no = emp.bank_ac_no
|
self.bank_account_no = emp.bank_ac_no
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def process_salary_based_on_working_days(self):
|
def process_salary_based_on_working_days(self):
|
||||||
self.get_working_days_details(lwp=self.leave_without_pay)
|
self.get_working_days_details(lwp=self.leave_without_pay)
|
||||||
self.calculate_net_pay()
|
self.calculate_net_pay()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def set_totals(self):
|
def set_totals(self):
|
||||||
self.gross_pay = 0.0
|
self.gross_pay = 0.0
|
||||||
if self.salary_slip_based_on_timesheet == 1:
|
if self.salary_slip_based_on_timesheet == 1:
|
||||||
|
|||||||
@ -4,12 +4,14 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
|
|
||||||
import frappe, unittest
|
import frappe, unittest
|
||||||
test_records = frappe.get_test_records('Project')
|
from frappe.utils import getdate, nowdate, add_days
|
||||||
test_ignore = ["Sales Order"]
|
|
||||||
|
|
||||||
from erpnext.projects.doctype.project_template.test_project_template import make_project_template
|
from erpnext.projects.doctype.project_template.test_project_template import make_project_template
|
||||||
from erpnext.projects.doctype.task.test_task import create_task
|
from erpnext.projects.doctype.task.test_task import create_task
|
||||||
from frappe.utils import getdate, nowdate, add_days
|
|
||||||
|
test_records = frappe.get_test_records('Project')
|
||||||
|
test_ignore = ["Sales Order"]
|
||||||
|
|
||||||
|
|
||||||
class TestProject(unittest.TestCase):
|
class TestProject(unittest.TestCase):
|
||||||
def test_project_with_template_having_no_parent_and_depend_tasks(self):
|
def test_project_with_template_having_no_parent_and_depend_tasks(self):
|
||||||
@ -112,7 +114,8 @@ def make_project(args):
|
|||||||
doctype = 'Project',
|
doctype = 'Project',
|
||||||
project_name = args.project_name,
|
project_name = args.project_name,
|
||||||
status = 'Open',
|
status = 'Open',
|
||||||
expected_start_date = args.start_date
|
expected_start_date = args.start_date,
|
||||||
|
company= args.company or '_Test Company'
|
||||||
))
|
))
|
||||||
|
|
||||||
if args.project_template_name:
|
if args.project_template_name:
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import frappe
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class QualityFeedback(Document):
|
class QualityFeedback(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def set_parameters(self):
|
def set_parameters(self):
|
||||||
if self.template and not getattr(self, 'parameters', []):
|
if self.template and not getattr(self, 'parameters', []):
|
||||||
for d in frappe.get_doc('Quality Feedback Template', self.template).parameters:
|
for d in frappe.get_doc('Quality Feedback Template', self.template).parameters:
|
||||||
|
|||||||
@ -50,6 +50,7 @@ class TaxExemption80GCertificate(Document):
|
|||||||
frappe.throw(_('Please set the {0} for company {1}').format(frappe.bold('PAN Number'),
|
frappe.throw(_('Please set the {0} for company {1}').format(frappe.bold('PAN Number'),
|
||||||
get_link_to_form('Company', self.company)))
|
get_link_to_form('Company', self.company)))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def set_company_address(self):
|
def set_company_address(self):
|
||||||
address = get_company_address(self.company)
|
address = get_company_address(self.company)
|
||||||
self.company_address = address.company_address
|
self.company_address = address.company_address
|
||||||
@ -70,6 +71,7 @@ class TaxExemption80GCertificate(Document):
|
|||||||
else:
|
else:
|
||||||
self.title = self.donor_name
|
self.title = self.donor_name
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_payments(self):
|
def get_payments(self):
|
||||||
if not self.member:
|
if not self.member:
|
||||||
frappe.throw(_('Please select a Member first.'))
|
frappe.throw(_('Please select a Member first.'))
|
||||||
|
|||||||
@ -64,6 +64,7 @@ class Quotation(SellingController):
|
|||||||
opp = frappe.get_doc("Opportunity", opportunity)
|
opp = frappe.get_doc("Opportunity", opportunity)
|
||||||
opp.set_status(status=status, update=True)
|
opp.set_status(status=status, update=True)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def declare_enquiry_lost(self, lost_reasons_list, detailed_reason=None):
|
def declare_enquiry_lost(self, lost_reasons_list, detailed_reason=None):
|
||||||
if not self.has_sales_order():
|
if not self.has_sales_order():
|
||||||
get_lost_reasons = frappe.get_list('Quotation Lost Reason',
|
get_lost_reasons = frappe.get_list('Quotation Lost Reason',
|
||||||
|
|||||||
@ -372,6 +372,7 @@ class SalesOrder(SellingController):
|
|||||||
self.indicator_color = "green"
|
self.indicator_color = "green"
|
||||||
self.indicator_title = _("Paid")
|
self.indicator_title = _("Paid")
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_work_order_items(self, for_raw_material_request=0):
|
def get_work_order_items(self, for_raw_material_request=0):
|
||||||
'''Returns items with BOM that already do not have a linked work order'''
|
'''Returns items with BOM that already do not have a linked work order'''
|
||||||
items = []
|
items = []
|
||||||
|
|||||||
@ -259,6 +259,7 @@ erpnext.company.setup_queries = function(frm) {
|
|||||||
["default_payroll_payable_account", {"root_type": "Liability"}],
|
["default_payroll_payable_account", {"root_type": "Liability"}],
|
||||||
["round_off_account", {"root_type": "Expense"}],
|
["round_off_account", {"root_type": "Expense"}],
|
||||||
["write_off_account", {"root_type": "Expense"}],
|
["write_off_account", {"root_type": "Expense"}],
|
||||||
|
["default_discount_account", {}],
|
||||||
["discount_allowed_account", {"root_type": "Expense"}],
|
["discount_allowed_account", {"root_type": "Expense"}],
|
||||||
["discount_received_account", {"root_type": "Income"}],
|
["discount_received_account", {"root_type": "Income"}],
|
||||||
["exchange_gain_loss_account", {"root_type": "Expense"}],
|
["exchange_gain_loss_account", {"root_type": "Expense"}],
|
||||||
@ -275,7 +276,7 @@ erpnext.company.setup_queries = function(frm) {
|
|||||||
["expenses_included_in_asset_valuation", {"account_type": "Expenses Included In Asset Valuation"}],
|
["expenses_included_in_asset_valuation", {"account_type": "Expenses Included In Asset Valuation"}],
|
||||||
["capital_work_in_progress_account", {"account_type": "Capital Work in Progress"}],
|
["capital_work_in_progress_account", {"account_type": "Capital Work in Progress"}],
|
||||||
["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed"}],
|
["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed"}],
|
||||||
["unrealized_profit_loss_account", {"root_type": "Liability"}]
|
["unrealized_profit_loss_account", {"root_type": "Liability"},]
|
||||||
], function(i, v) {
|
], function(i, v) {
|
||||||
erpnext.company.set_custom_query(frm, v);
|
erpnext.company.set_custom_query(frm, v);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -59,6 +59,7 @@
|
|||||||
"default_deferred_expense_account",
|
"default_deferred_expense_account",
|
||||||
"default_payroll_payable_account",
|
"default_payroll_payable_account",
|
||||||
"default_expense_claim_payable_account",
|
"default_expense_claim_payable_account",
|
||||||
|
"default_discount_account",
|
||||||
"section_break_22",
|
"section_break_22",
|
||||||
"cost_center",
|
"cost_center",
|
||||||
"column_break_26",
|
"column_break_26",
|
||||||
@ -733,6 +734,12 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Unrealized Profit / Loss Account",
|
"label": "Unrealized Profit / Loss Account",
|
||||||
"options": "Account"
|
"options": "Account"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "default_discount_account",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Default Payment Discount Account",
|
||||||
|
"options": "Account"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-building",
|
"icon": "fa fa-building",
|
||||||
|
|||||||
@ -66,6 +66,7 @@ class Company(NestedSet):
|
|||||||
if frappe.db.sql("select abbr from tabCompany where name!=%s and abbr=%s", (self.name, self.abbr)):
|
if frappe.db.sql("select abbr from tabCompany where name!=%s and abbr=%s", (self.name, self.abbr)):
|
||||||
frappe.throw(_("Abbreviation already used for another company"))
|
frappe.throw(_("Abbreviation already used for another company"))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def create_default_tax_template(self):
|
def create_default_tax_template(self):
|
||||||
from erpnext.setup.setup_wizard.operations.taxes_setup import create_sales_tax
|
from erpnext.setup.setup_wizard.operations.taxes_setup import create_sales_tax
|
||||||
create_sales_tax({
|
create_sales_tax({
|
||||||
|
|||||||
@ -24,6 +24,7 @@ class EmailDigest(Document):
|
|||||||
self._accounts = {}
|
self._accounts = {}
|
||||||
self.currency = frappe.db.get_value('Company', self.company, "default_currency")
|
self.currency = frappe.db.get_value('Company', self.company, "default_currency")
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_users(self):
|
def get_users(self):
|
||||||
"""get list of users"""
|
"""get list of users"""
|
||||||
user_list = frappe.db.sql("""
|
user_list = frappe.db.sql("""
|
||||||
@ -41,6 +42,7 @@ class EmailDigest(Document):
|
|||||||
|
|
||||||
frappe.response['user_list'] = user_list
|
frappe.response['user_list'] = user_list
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def send(self):
|
def send(self):
|
||||||
# send email only to enabled users
|
# send email only to enabled users
|
||||||
valid_users = [p[0] for p in frappe.db.sql("""select name from `tabUser`
|
valid_users = [p[0] for p in frappe.db.sql("""select name from `tabUser`
|
||||||
|
|||||||
@ -50,6 +50,7 @@ class GlobalDefaults(Document):
|
|||||||
# clear cache
|
# clear cache
|
||||||
frappe.clear_cache()
|
frappe.clear_cache()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_defaults(self):
|
def get_defaults(self):
|
||||||
return frappe.defaults.get_defaults()
|
return frappe.defaults.get_defaults()
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@ from frappe.core.doctype.doctype.doctype import validate_series
|
|||||||
class NamingSeriesNotSetError(frappe.ValidationError): pass
|
class NamingSeriesNotSetError(frappe.ValidationError): pass
|
||||||
|
|
||||||
class NamingSeries(Document):
|
class NamingSeries(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def get_transactions(self, arg=None):
|
def get_transactions(self, arg=None):
|
||||||
doctypes = list(set(frappe.db.sql_list("""select parent
|
doctypes = list(set(frappe.db.sql_list("""select parent
|
||||||
from `tabDocField` df where fieldname='naming_series'""")
|
from `tabDocField` df where fieldname='naming_series'""")
|
||||||
@ -53,6 +54,7 @@ class NamingSeries(Document):
|
|||||||
options = list(filter(lambda x: x, [cstr(n).strip() for n in ol]))
|
options = list(filter(lambda x: x, [cstr(n).strip() for n in ol]))
|
||||||
return options
|
return options
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def update_series(self, arg=None):
|
def update_series(self, arg=None):
|
||||||
"""update series list"""
|
"""update series list"""
|
||||||
self.validate_series_set()
|
self.validate_series_set()
|
||||||
@ -139,10 +141,12 @@ class NamingSeries(Document):
|
|||||||
if not re.match("^[\w\- /.#{}]*$", n, re.UNICODE):
|
if not re.match("^[\w\- /.#{}]*$", n, re.UNICODE):
|
||||||
throw(_('Special Characters except "-", "#", ".", "/", "{" and "}" not allowed in naming series'))
|
throw(_('Special Characters except "-", "#", ".", "/", "{" and "}" not allowed in naming series'))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_options(self, arg=None):
|
def get_options(self, arg=None):
|
||||||
if frappe.get_meta(arg or self.select_doc_for_series).get_field("naming_series"):
|
if frappe.get_meta(arg or self.select_doc_for_series).get_field("naming_series"):
|
||||||
return frappe.get_meta(arg or self.select_doc_for_series).get_field("naming_series").options
|
return frappe.get_meta(arg or self.select_doc_for_series).get_field("naming_series").options
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_current(self, arg=None):
|
def get_current(self, arg=None):
|
||||||
"""get series current"""
|
"""get series current"""
|
||||||
if self.prefix:
|
if self.prefix:
|
||||||
|
|||||||
@ -90,6 +90,7 @@ class DeliveryTrip(Document):
|
|||||||
delivery_notes = [get_link_to_form("Delivery Note", note) for note in delivery_notes]
|
delivery_notes = [get_link_to_form("Delivery Note", note) for note in delivery_notes]
|
||||||
frappe.msgprint(_("Delivery Notes {0} updated").format(", ".join(delivery_notes)))
|
frappe.msgprint(_("Delivery Notes {0} updated").format(", ".join(delivery_notes)))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def process_route(self, optimize):
|
def process_route(self, optimize):
|
||||||
"""
|
"""
|
||||||
Estimate the arrival times for each stop in the Delivery Trip.
|
Estimate the arrival times for each stop in the Delivery Trip.
|
||||||
|
|||||||
@ -50,6 +50,7 @@ class Item(WebsiteGenerator):
|
|||||||
self.set_onload('stock_exists', self.stock_ledger_created())
|
self.set_onload('stock_exists', self.stock_ledger_created())
|
||||||
self.set_asset_naming_series()
|
self.set_asset_naming_series()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def set_asset_naming_series(self):
|
def set_asset_naming_series(self):
|
||||||
if not hasattr(self, '_asset_naming_series'):
|
if not hasattr(self, '_asset_naming_series'):
|
||||||
from erpnext.assets.doctype.asset.asset import get_asset_naming_series
|
from erpnext.assets.doctype.asset.asset import get_asset_naming_series
|
||||||
@ -706,6 +707,7 @@ class Item(WebsiteGenerator):
|
|||||||
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", existing_allow_negative_stock)
|
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", existing_allow_negative_stock)
|
||||||
frappe.db.auto_commit_on_many_writes = 0
|
frappe.db.auto_commit_on_many_writes = 0
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def copy_specification_from_item_group(self):
|
def copy_specification_from_item_group(self):
|
||||||
self.set("website_specifications", [])
|
self.set("website_specifications", [])
|
||||||
if self.item_group:
|
if self.item_group:
|
||||||
|
|||||||
@ -12,6 +12,7 @@ from erpnext.accounts.doctype.account.account import get_account_currency
|
|||||||
from erpnext.controllers.taxes_and_totals import init_landed_taxes_and_totals
|
from erpnext.controllers.taxes_and_totals import init_landed_taxes_and_totals
|
||||||
|
|
||||||
class LandedCostVoucher(Document):
|
class LandedCostVoucher(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def get_items_from_purchase_receipts(self):
|
def get_items_from_purchase_receipts(self):
|
||||||
self.set("items", [])
|
self.set("items", [])
|
||||||
for pr in self.get("purchase_receipts"):
|
for pr in self.get("purchase_receipts"):
|
||||||
|
|||||||
@ -152,6 +152,7 @@ class PackingSlip(Document):
|
|||||||
|
|
||||||
return cint(recommended_case_no[0][0]) + 1
|
return cint(recommended_case_no[0][0]) + 1
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_items(self):
|
def get_items(self):
|
||||||
self.set("items", [])
|
self.set("items", [])
|
||||||
|
|
||||||
|
|||||||
@ -33,6 +33,7 @@ class PickList(Document):
|
|||||||
frappe.throw(_('For item {0} at row {1}, count of serial numbers does not match with the picked quantity')
|
frappe.throw(_('For item {0} at row {1}, count of serial numbers does not match with the picked quantity')
|
||||||
.format(frappe.bold(item.item_code), frappe.bold(item.idx)), title=_("Quantity Mismatch"))
|
.format(frappe.bold(item.item_code), frappe.bold(item.idx)), title=_("Quantity Mismatch"))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def set_item_locations(self, save=False):
|
def set_item_locations(self, save=False):
|
||||||
items = self.aggregate_item_qty()
|
items = self.aggregate_item_qty()
|
||||||
self.item_location_map = frappe._dict()
|
self.item_location_map = frappe._dict()
|
||||||
|
|||||||
@ -18,6 +18,7 @@ class QualityInspection(Document):
|
|||||||
if self.readings:
|
if self.readings:
|
||||||
self.inspect_and_set_status()
|
self.inspect_and_set_status()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_item_specification_details(self):
|
def get_item_specification_details(self):
|
||||||
if not self.quality_inspection_template:
|
if not self.quality_inspection_template:
|
||||||
self.quality_inspection_template = frappe.db.get_value('Item',
|
self.quality_inspection_template = frappe.db.get_value('Item',
|
||||||
@ -32,6 +33,7 @@ class QualityInspection(Document):
|
|||||||
child.update(d)
|
child.update(d)
|
||||||
child.status = "Accepted"
|
child.status = "Accepted"
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_quality_inspection_template(self):
|
def get_quality_inspection_template(self):
|
||||||
template = ''
|
template = ''
|
||||||
if self.bom_no:
|
if self.bom_no:
|
||||||
|
|||||||
@ -39,6 +39,7 @@ class RepostItemValuation(Document):
|
|||||||
frappe.enqueue(repost, timeout=1800, queue='long',
|
frappe.enqueue(repost, timeout=1800, queue='long',
|
||||||
job_name='repost_sle', now=frappe.flags.in_test, doc=self)
|
job_name='repost_sle', now=frappe.flags.in_test, doc=self)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def restart_reposting(self):
|
def restart_reposting(self):
|
||||||
self.set_status('Queued')
|
self.set_status('Queued')
|
||||||
frappe.enqueue(repost, timeout=1800, queue='long',
|
frappe.enqueue(repost, timeout=1800, queue='long',
|
||||||
|
|||||||
@ -839,6 +839,7 @@ class StockEntry(StockController):
|
|||||||
if not pro_doc.operations:
|
if not pro_doc.operations:
|
||||||
pro_doc.set_actual_dates()
|
pro_doc.set_actual_dates()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_item_details(self, args=None, for_update=False):
|
def get_item_details(self, args=None, for_update=False):
|
||||||
item = frappe.db.sql("""select i.name, i.stock_uom, i.description, i.image, i.item_name, i.item_group,
|
item = frappe.db.sql("""select i.name, i.stock_uom, i.description, i.image, i.item_name, i.item_group,
|
||||||
i.has_batch_no, i.sample_quantity, i.has_serial_no, i.allow_alternative_item,
|
i.has_batch_no, i.sample_quantity, i.has_serial_no, i.allow_alternative_item,
|
||||||
@ -913,6 +914,7 @@ class StockEntry(StockController):
|
|||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def set_items_for_stock_in(self):
|
def set_items_for_stock_in(self):
|
||||||
self.items = []
|
self.items = []
|
||||||
|
|
||||||
@ -937,6 +939,7 @@ class StockEntry(StockController):
|
|||||||
'batch_no': d.batch_no
|
'batch_no': d.batch_no
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_items(self):
|
def get_items(self):
|
||||||
self.set('items', [])
|
self.set('items', [])
|
||||||
self.validate_work_order()
|
self.validate_work_order()
|
||||||
|
|||||||
@ -110,7 +110,7 @@ def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=Tru
|
|||||||
get_gross_profit(out)
|
get_gross_profit(out)
|
||||||
if args.doctype == 'Material Request':
|
if args.doctype == 'Material Request':
|
||||||
out.rate = args.rate or out.price_list_rate
|
out.rate = args.rate or out.price_list_rate
|
||||||
out.amount = flt(args.qty * out.rate)
|
out.amount = flt(args.qty) * flt(out.rate)
|
||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|||||||
@ -165,6 +165,7 @@ class Issue(Document):
|
|||||||
communication.ignore_mandatory = True
|
communication.ignore_mandatory = True
|
||||||
communication.save()
|
communication.save()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def split_issue(self, subject, communication_id):
|
def split_issue(self, subject, communication_id):
|
||||||
# Bug: Pressing enter doesn't send subject
|
# Bug: Pressing enter doesn't send subject
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
@ -259,6 +260,7 @@ class Issue(Document):
|
|||||||
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 changed to {0}.").format(self.service_level_agreement))
|
frappe.msgprint(_("Service Level Agreement has been changed to {0}.").format(self.service_level_agreement))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def reset_service_level_agreement(self, reason, user):
|
def reset_service_level_agreement(self, reason, user):
|
||||||
if not frappe.db.get_single_value("Support Settings", "allow_resetting_service_level_agreement"):
|
if not frappe.db.get_single_value("Support Settings", "allow_resetting_service_level_agreement"):
|
||||||
frappe.throw(_("Allow Resetting Service Level Agreement from Support Settings."))
|
frappe.throw(_("Allow Resetting Service Level Agreement from Support Settings."))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user