Merge branch 'develop' into payment-terms
@ -51,11 +51,14 @@ before_script:
|
|||||||
- bench start &
|
- bench start &
|
||||||
- sleep 10
|
- sleep 10
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
include:
|
||||||
|
- stage: test
|
||||||
script:
|
script:
|
||||||
- set -e
|
- set -e
|
||||||
- bench run-tests
|
- bench run-tests
|
||||||
- sleep 5
|
- # stage
|
||||||
- bench reinstall --yes
|
script:
|
||||||
- bench --verbose run-setup-wizard-ui-test
|
- bench --verbose run-setup-wizard-ui-test
|
||||||
- bench execute erpnext.setup.utils.enable_all_roles_and_domains
|
- bench execute erpnext.setup.utils.enable_all_roles_and_domains
|
||||||
- bench run-ui-tests --app erpnext
|
- bench run-ui-tests --app erpnext
|
||||||
|
@ -4,7 +4,7 @@ import inspect
|
|||||||
import frappe
|
import frappe
|
||||||
from erpnext.hooks import regional_overrides
|
from erpnext.hooks import regional_overrides
|
||||||
|
|
||||||
__version__ = '8.11.2'
|
__version__ = '8.11.4'
|
||||||
|
|
||||||
def get_default_company(user=None):
|
def get_default_company(user=None):
|
||||||
'''Get default company for user'''
|
'''Get default company for user'''
|
||||||
|
@ -36,7 +36,7 @@ class GLEntry(Document):
|
|||||||
validate_balance_type(self.account, adv_adj)
|
validate_balance_type(self.account, adv_adj)
|
||||||
|
|
||||||
# Update outstanding amt on against voucher
|
# Update outstanding amt on against voucher
|
||||||
if self.against_voucher_type in ['Journal Entry', 'Sales Invoice', 'Purchase Invoice'] \
|
if self.against_voucher_type in ['Journal Entry', 'Sales Invoice', 'Purchase Invoice', 'Fees'] \
|
||||||
and self.against_voucher and update_outstanding == 'Yes' and not from_repost:
|
and self.against_voucher and update_outstanding == 'Yes' and not from_repost:
|
||||||
update_outstanding_amt(self.account, self.party_type, self.party, self.against_voucher_type,
|
update_outstanding_amt(self.account, self.party_type, self.party, self.against_voucher_type,
|
||||||
self.against_voucher)
|
self.against_voucher)
|
||||||
@ -196,7 +196,7 @@ def update_outstanding_amt(account, party_type, party, against_voucher_type, aga
|
|||||||
frappe.throw(_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal)))
|
frappe.throw(_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal)))
|
||||||
|
|
||||||
# Update outstanding amt on against voucher
|
# Update outstanding amt on against voucher
|
||||||
if against_voucher_type in ["Sales Invoice", "Purchase Invoice"]:
|
if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]:
|
||||||
ref_doc = frappe.get_doc(against_voucher_type, against_voucher)
|
ref_doc = frappe.get_doc(against_voucher_type, against_voucher)
|
||||||
ref_doc.db_set('outstanding_amount', bal)
|
ref_doc.db_set('outstanding_amount', bal)
|
||||||
ref_doc.set_status(update=True)
|
ref_doc.set_status(update=True)
|
||||||
|
@ -12,7 +12,8 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
|
|
||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
frm.set_query("paid_from", function() {
|
frm.set_query("paid_from", function() {
|
||||||
var party_account_type = frm.doc.party_type=="Customer" ? "Receivable" : "Payable";
|
var party_account_type = in_list(["Customer", "Student"], frm.doc.party_type) ?
|
||||||
|
"Receivable" : "Payable";
|
||||||
var account_types = in_list(["Pay", "Internal Transfer"], frm.doc.payment_type) ?
|
var account_types = in_list(["Pay", "Internal Transfer"], frm.doc.payment_type) ?
|
||||||
["Bank", "Cash"] : party_account_type;
|
["Bank", "Cash"] : party_account_type;
|
||||||
|
|
||||||
@ -28,13 +29,14 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
frm.set_query("party_type", function() {
|
frm.set_query("party_type", function() {
|
||||||
return{
|
return{
|
||||||
"filters": {
|
"filters": {
|
||||||
"name": ["in",["Customer","Supplier", "Employee"]],
|
"name": ["in",["Customer","Supplier", "Employee", "Student"]],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.set_query("paid_to", function() {
|
frm.set_query("paid_to", function() {
|
||||||
var party_account_type = frm.doc.party_type=="Customer" ? "Receivable" : "Payable";
|
var party_account_type = in_list(["Customer", "Student"], frm.doc.party_type) ?
|
||||||
|
"Receivable" : "Payable";
|
||||||
var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_type) ?
|
var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_type) ?
|
||||||
["Bank", "Cash"] : party_account_type;
|
["Bank", "Cash"] : party_account_type;
|
||||||
|
|
||||||
@ -72,6 +74,8 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
var doctypes = ["Purchase Order", "Purchase Invoice", "Journal Entry"];
|
var doctypes = ["Purchase Order", "Purchase Invoice", "Journal Entry"];
|
||||||
} else if (frm.doc.party_type=="Employee") {
|
} else if (frm.doc.party_type=="Employee") {
|
||||||
var doctypes = ["Expense Claim", "Journal Entry"];
|
var doctypes = ["Expense Claim", "Journal Entry"];
|
||||||
|
} else if (frm.doc.party_type=="Student") {
|
||||||
|
var doctypes = ["Fees"];
|
||||||
} else {
|
} else {
|
||||||
var doctypes = ["Journal Entry"];
|
var doctypes = ["Journal Entry"];
|
||||||
}
|
}
|
||||||
@ -85,7 +89,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
child = locals[cdt][cdn];
|
child = locals[cdt][cdn];
|
||||||
filters = {"docstatus": 1, "company": doc.company};
|
filters = {"docstatus": 1, "company": doc.company};
|
||||||
party_type_doctypes = ['Sales Invoice', 'Sales Order', 'Purchase Invoice',
|
party_type_doctypes = ['Sales Invoice', 'Sales Order', 'Purchase Invoice',
|
||||||
'Purchase Order', 'Expense Claim'];
|
'Purchase Order', 'Expense Claim', 'Fees'];
|
||||||
|
|
||||||
if (in_list(party_type_doctypes, child.reference_doctype)) {
|
if (in_list(party_type_doctypes, child.reference_doctype)) {
|
||||||
filters[doc.party_type.toLowerCase()] = doc.party;
|
filters[doc.party_type.toLowerCase()] = doc.party;
|
||||||
@ -207,20 +211,14 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
frm.set_value(field, null);
|
frm.set_value(field, null);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if(!frm.doc.party)
|
if(frm.doc.party) {
|
||||||
{
|
|
||||||
if (frm.doc.payment_type=="Receive"){
|
|
||||||
frm.set_value("party_type", "Customer");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
frm.events.party(frm);
|
frm.events.party(frm);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(frm.doc.mode_of_payment)
|
if(frm.doc.mode_of_payment) {
|
||||||
frm.events.mode_of_payment(frm);
|
frm.events.mode_of_payment(frm);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
party_type: function(frm) {
|
party_type: function(frm) {
|
||||||
@ -254,6 +252,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
date: frm.doc.posting_date
|
date: frm.doc.posting_date
|
||||||
},
|
},
|
||||||
callback: function(r, rt) {
|
callback: function(r, rt) {
|
||||||
|
console.log(r, rt);
|
||||||
if(r.message) {
|
if(r.message) {
|
||||||
if(frm.doc.payment_type == "Receive") {
|
if(frm.doc.payment_type == "Receive") {
|
||||||
frm.set_value("paid_from", r.message.party_account);
|
frm.set_value("paid_from", r.message.party_account);
|
||||||
@ -502,6 +501,8 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
c.due_date = d.due_date
|
c.due_date = d.due_date
|
||||||
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;
|
||||||
|
|
||||||
if(!in_list(["Sales Order", "Purchase Order", "Expense Claim"], d.voucher_type)) {
|
if(!in_list(["Sales Order", "Purchase Order", "Expense Claim"], d.voucher_type)) {
|
||||||
if(flt(d.outstanding_amount) > 0)
|
if(flt(d.outstanding_amount) > 0)
|
||||||
total_positive_outstanding += flt(d.outstanding_amount);
|
total_positive_outstanding += flt(d.outstanding_amount);
|
||||||
|
@ -100,8 +100,8 @@ class PaymentEntry(AccountsController):
|
|||||||
if not self.party:
|
if not self.party:
|
||||||
frappe.throw(_("Party is mandatory"))
|
frappe.throw(_("Party is mandatory"))
|
||||||
|
|
||||||
self.party_name = frappe.db.get_value(self.party_type, self.party,
|
_party_name = "title" if self.party_type == "Student" else self.party_type.lower() + "_name"
|
||||||
self.party_type.lower() + "_name")
|
self.party_name = frappe.db.get_value(self.party_type, self.party, _party_name)
|
||||||
|
|
||||||
if self.party:
|
if self.party:
|
||||||
if not self.party_balance:
|
if not self.party_balance:
|
||||||
@ -149,7 +149,7 @@ class PaymentEntry(AccountsController):
|
|||||||
frappe.throw(_("Invalid {0}: {1}").format(self.party_type, self.party))
|
frappe.throw(_("Invalid {0}: {1}").format(self.party_type, self.party))
|
||||||
|
|
||||||
if self.party_account:
|
if self.party_account:
|
||||||
party_account_type = "Receivable" if self.party_type=="Customer" else "Payable"
|
party_account_type = "Receivable" if self.party_type in ("Customer", "Student") else "Payable"
|
||||||
self.validate_account_type(self.party_account, [party_account_type])
|
self.validate_account_type(self.party_account, [party_account_type])
|
||||||
|
|
||||||
def validate_bank_accounts(self):
|
def validate_bank_accounts(self):
|
||||||
@ -182,7 +182,9 @@ class PaymentEntry(AccountsController):
|
|||||||
frappe.throw(_("{0} is mandatory").format(self.meta.get_label(field)))
|
frappe.throw(_("{0} is mandatory").format(self.meta.get_label(field)))
|
||||||
|
|
||||||
def validate_reference_documents(self):
|
def validate_reference_documents(self):
|
||||||
if self.party_type == "Customer":
|
if self.party_type == "Student":
|
||||||
|
valid_reference_doctypes = ("Fees")
|
||||||
|
elif self.party_type == "Customer":
|
||||||
valid_reference_doctypes = ("Sales Order", "Sales Invoice", "Journal Entry")
|
valid_reference_doctypes = ("Sales Order", "Sales Invoice", "Journal Entry")
|
||||||
elif self.party_type == "Supplier":
|
elif self.party_type == "Supplier":
|
||||||
valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry")
|
valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry")
|
||||||
@ -209,9 +211,11 @@ class PaymentEntry(AccountsController):
|
|||||||
else:
|
else:
|
||||||
self.validate_journal_entry()
|
self.validate_journal_entry()
|
||||||
|
|
||||||
if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Expense Claim"):
|
if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Expense Claim", "Fees"):
|
||||||
if self.party_type == "Customer":
|
if self.party_type == "Customer":
|
||||||
ref_party_account = ref_doc.debit_to
|
ref_party_account = ref_doc.debit_to
|
||||||
|
elif self.party_type == "Student":
|
||||||
|
ref_party_account = ref_doc.receivable_account
|
||||||
elif self.party_type=="Supplier":
|
elif self.party_type=="Supplier":
|
||||||
ref_party_account = ref_doc.credit_to
|
ref_party_account = ref_doc.credit_to
|
||||||
elif self.party_type=="Employee":
|
elif self.party_type=="Employee":
|
||||||
@ -398,7 +402,7 @@ class PaymentEntry(AccountsController):
|
|||||||
"account_currency": self.party_account_currency
|
"account_currency": self.party_account_currency
|
||||||
})
|
})
|
||||||
|
|
||||||
dr_or_cr = "credit" if self.party_type == "Customer" else "debit"
|
dr_or_cr = "credit" if self.party_type in ["Customer", "Student"] else "debit"
|
||||||
|
|
||||||
for d in self.get("references"):
|
for d in self.get("references"):
|
||||||
gle = party_gl_dict.copy()
|
gle = party_gl_dict.copy()
|
||||||
@ -484,8 +488,13 @@ class PaymentEntry(AccountsController):
|
|||||||
doc = frappe.get_doc("Expense Claim", d.reference_name)
|
doc = frappe.get_doc("Expense Claim", d.reference_name)
|
||||||
update_reimbursed_amount(doc)
|
update_reimbursed_amount(doc)
|
||||||
|
|
||||||
|
def on_recurring(self, reference_doc, subscription_doc):
|
||||||
|
self.reference_no = reference_doc.name
|
||||||
|
self.reference_date = nowdate()
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_outstanding_reference_documents(args):
|
def get_outstanding_reference_documents(args):
|
||||||
|
if isinstance(args, basestring):
|
||||||
args = json.loads(args)
|
args = json.loads(args)
|
||||||
|
|
||||||
party_account_currency = get_account_currency(args.get("party_account"))
|
party_account_currency = get_account_currency(args.get("party_account"))
|
||||||
@ -494,10 +503,12 @@ def get_outstanding_reference_documents(args):
|
|||||||
# Get negative outstanding sales /purchase invoices
|
# Get negative outstanding sales /purchase invoices
|
||||||
total_field = "base_grand_total" if party_account_currency == company_currency else "grand_total"
|
total_field = "base_grand_total" if party_account_currency == company_currency else "grand_total"
|
||||||
|
|
||||||
|
negative_outstanding_invoices = []
|
||||||
|
if (args.get("party_type") != "Student"):
|
||||||
negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"),
|
negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"),
|
||||||
args.get("party"), args.get("party_account"), total_field)
|
args.get("party"), args.get("party_account"), total_field)
|
||||||
|
|
||||||
# Get positive outstanding sales /purchase invoices
|
# Get positive outstanding sales /purchase invoices/ Fees
|
||||||
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"))
|
args.get("party_account"))
|
||||||
|
|
||||||
@ -510,10 +521,14 @@ def get_outstanding_reference_documents(args):
|
|||||||
d["exchange_rate"] = get_exchange_rate(
|
d["exchange_rate"] = get_exchange_rate(
|
||||||
party_account_currency, company_currency, d.posting_date
|
party_account_currency, company_currency, d.posting_date
|
||||||
)
|
)
|
||||||
|
if d.voucher_type in ("Purchase Invoice"):
|
||||||
|
d["bill_no"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "bill_no")
|
||||||
|
|
||||||
# Get all SO / PO which are not fully billed or aginst which full advance not paid
|
# Get all SO / PO which are not fully billed or aginst which full advance not paid
|
||||||
orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"), args.get("party"),
|
orders_to_be_billed = []
|
||||||
party_account_currency, company_currency)
|
if (args.get("party_type") != "Student"):
|
||||||
|
orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"),
|
||||||
|
args.get("party"), party_account_currency, company_currency)
|
||||||
|
|
||||||
return negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed
|
return negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed
|
||||||
|
|
||||||
@ -628,7 +643,11 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
|
|||||||
total_amount = outstanding_amount = exchange_rate = None
|
total_amount = outstanding_amount = exchange_rate = None
|
||||||
ref_doc = frappe.get_doc(reference_doctype, reference_name)
|
ref_doc = frappe.get_doc(reference_doctype, reference_name)
|
||||||
|
|
||||||
if reference_doctype != "Journal Entry":
|
if reference_doctype == "Fees":
|
||||||
|
total_amount = ref_doc.get("grand_total")
|
||||||
|
exchange_rate = 1
|
||||||
|
outstanding_amount = ref_doc.get("outstanding_amount")
|
||||||
|
elif reference_doctype != "Journal Entry":
|
||||||
if party_account_currency == ref_doc.company_currency:
|
if party_account_currency == ref_doc.company_currency:
|
||||||
if ref_doc.doctype == "Expense Claim":
|
if ref_doc.doctype == "Expense Claim":
|
||||||
total_amount = ref_doc.total_sanctioned_amount
|
total_amount = ref_doc.total_sanctioned_amount
|
||||||
@ -671,19 +690,23 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
|||||||
party_type = "Supplier"
|
party_type = "Supplier"
|
||||||
elif dt in ("Expense Claim"):
|
elif dt in ("Expense Claim"):
|
||||||
party_type = "Employee"
|
party_type = "Employee"
|
||||||
|
elif dt in ("Fees"):
|
||||||
|
party_type = "Student"
|
||||||
|
|
||||||
# party account
|
# party account
|
||||||
if dt == "Sales Invoice":
|
if dt == "Sales Invoice":
|
||||||
party_account = doc.debit_to
|
party_account = doc.debit_to
|
||||||
elif dt == "Purchase Invoice":
|
elif dt == "Purchase Invoice":
|
||||||
party_account = doc.credit_to
|
party_account = doc.credit_to
|
||||||
|
elif dt == "Fees":
|
||||||
|
party_account = doc.receivable_account
|
||||||
else:
|
else:
|
||||||
party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company)
|
party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company)
|
||||||
|
|
||||||
party_account_currency = doc.get("party_account_currency") or get_account_currency(party_account)
|
party_account_currency = doc.get("party_account_currency") or get_account_currency(party_account)
|
||||||
|
|
||||||
# payment type
|
# payment type
|
||||||
if (dt == "Sales Order" or (dt=="Sales Invoice" and doc.outstanding_amount > 0)) \
|
if (dt == "Sales Order" or (dt in ("Sales Invoice", "Fees") and doc.outstanding_amount > 0)) \
|
||||||
or (dt=="Purchase Invoice" and doc.outstanding_amount < 0):
|
or (dt=="Purchase Invoice" and doc.outstanding_amount < 0):
|
||||||
payment_type = "Receive"
|
payment_type = "Receive"
|
||||||
else:
|
else:
|
||||||
@ -699,6 +722,9 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
|||||||
elif dt in ("Expense Claim"):
|
elif dt in ("Expense Claim"):
|
||||||
grand_total = doc.total_sanctioned_amount
|
grand_total = doc.total_sanctioned_amount
|
||||||
outstanding_amount = doc.total_sanctioned_amount - doc.total_amount_reimbursed
|
outstanding_amount = doc.total_sanctioned_amount - doc.total_amount_reimbursed
|
||||||
|
elif dt == "Fees":
|
||||||
|
grand_total = doc.grand_total
|
||||||
|
outstanding_amount = doc.outstanding_amount
|
||||||
else:
|
else:
|
||||||
total_field = "base_grand_total" if party_account_currency == doc.company_currency else "grand_total"
|
total_field = "base_grand_total" if party_account_currency == doc.company_currency else "grand_total"
|
||||||
grand_total = flt(doc.get(total_field))
|
grand_total = flt(doc.get(total_field))
|
||||||
|
@ -9,9 +9,9 @@ QUnit.test("test payment entry", function(assert) {
|
|||||||
{customer: 'Test Customer 1'},
|
{customer: 'Test Customer 1'},
|
||||||
{items: [
|
{items: [
|
||||||
[
|
[
|
||||||
|
{'item_code': 'Test Product 1'},
|
||||||
{'qty': 1},
|
{'qty': 1},
|
||||||
{'rate': 101},
|
{'rate': 101},
|
||||||
{'item_code': 'Test Product 1'},
|
|
||||||
]
|
]
|
||||||
]}
|
]}
|
||||||
]);
|
]);
|
||||||
@ -19,11 +19,12 @@ QUnit.test("test payment entry", function(assert) {
|
|||||||
() => cur_frm.save(),
|
() => cur_frm.save(),
|
||||||
() => frappe.tests.click_button('Submit'),
|
() => frappe.tests.click_button('Submit'),
|
||||||
() => frappe.tests.click_button('Yes'),
|
() => frappe.tests.click_button('Yes'),
|
||||||
() => frappe.timeout(0.5),
|
() => frappe.timeout(1),
|
||||||
() => frappe.tests.click_button('Close'),
|
() => frappe.tests.click_button('Close'),
|
||||||
() => frappe.timeout(0.5),
|
() => frappe.timeout(1),
|
||||||
() => frappe.click_button('Make'),
|
() => frappe.click_button('Make'),
|
||||||
() => frappe.click_link('Payment', 1),
|
() => frappe.timeout(1),
|
||||||
|
() => frappe.click_link('Payment'),
|
||||||
() => frappe.timeout(2),
|
() => frappe.timeout(2),
|
||||||
() => {
|
() => {
|
||||||
assert.equal(frappe.get_route()[1], 'Payment Entry',
|
assert.equal(frappe.get_route()[1], 'Payment Entry',
|
||||||
@ -35,16 +36,19 @@ QUnit.test("test payment entry", function(assert) {
|
|||||||
assert.equal(cur_frm.doc.references[0].allocated_amount, 101,
|
assert.equal(cur_frm.doc.references[0].allocated_amount, 101,
|
||||||
'amount allocated against sales invoice');
|
'amount allocated against sales invoice');
|
||||||
},
|
},
|
||||||
|
() => frappe.timeout(1),
|
||||||
() => cur_frm.set_value('paid_amount', 100),
|
() => cur_frm.set_value('paid_amount', 100),
|
||||||
|
() => frappe.timeout(1),
|
||||||
() => {
|
() => {
|
||||||
cur_frm.doc.references[0].allocated_amount = 101;
|
frappe.model.set_value("Payment Entry Reference", cur_frm.doc.references[0].name,
|
||||||
|
"allocated_amount", 101);
|
||||||
},
|
},
|
||||||
|
() => frappe.timeout(1),
|
||||||
() => frappe.click_button('Write Off Difference Amount'),
|
() => frappe.click_button('Write Off Difference Amount'),
|
||||||
|
() => frappe.timeout(1),
|
||||||
() => {
|
() => {
|
||||||
assert.equal(cur_frm.doc.difference_amount, 0,
|
assert.equal(cur_frm.doc.difference_amount, 0, 'difference amount is zero');
|
||||||
'difference amount is zero');
|
assert.equal(cur_frm.doc.deductions[0].amount, 1, 'Write off amount = 1');
|
||||||
assert.equal(cur_frm.doc.deductions[0].amount, 1,
|
|
||||||
'Write off amount = 1');
|
|
||||||
},
|
},
|
||||||
() => done()
|
() => done()
|
||||||
]);
|
]);
|
||||||
|
@ -7,7 +7,7 @@ QUnit.test("test payment entry", function(assert) {
|
|||||||
() => {
|
() => {
|
||||||
return frappe.tests.make('Sales Invoice', [
|
return frappe.tests.make('Sales Invoice', [
|
||||||
{customer: 'Test Customer 1'},
|
{customer: 'Test Customer 1'},
|
||||||
{company: '_Test Company'},
|
{company: 'For Testing'},
|
||||||
{currency: 'INR'},
|
{currency: 'INR'},
|
||||||
{selling_price_list: '_Test Price List'},
|
{selling_price_list: '_Test Price List'},
|
||||||
{items: [
|
{items: [
|
||||||
@ -29,12 +29,12 @@ QUnit.test("test payment entry", function(assert) {
|
|||||||
() => frappe.timeout(1),
|
() => frappe.timeout(1),
|
||||||
() => frappe.click_link('Payment'),
|
() => frappe.click_link('Payment'),
|
||||||
() => frappe.timeout(2),
|
() => frappe.timeout(2),
|
||||||
() => cur_frm.set_value("paid_to", "_Test Cash - _TC"),
|
() => cur_frm.set_value("paid_to", "_Test Cash - FT"),
|
||||||
() => frappe.timeout(0.5),
|
() => frappe.timeout(0.5),
|
||||||
() => {
|
() => {
|
||||||
assert.equal(frappe.get_route()[1], 'Payment Entry', 'made payment entry');
|
assert.equal(frappe.get_route()[1], 'Payment Entry', 'made payment entry');
|
||||||
assert.equal(cur_frm.doc.party, 'Test Customer 1', 'customer set in payment entry');
|
assert.equal(cur_frm.doc.party, 'Test Customer 1', 'customer set in payment entry');
|
||||||
assert.equal(cur_frm.doc.paid_from, 'Debtors - _TC', 'customer account set in payment entry');
|
assert.equal(cur_frm.doc.paid_from, 'Debtors - FT', 'customer account set in payment entry');
|
||||||
assert.equal(cur_frm.doc.paid_amount, 100, 'paid amount set in payment entry');
|
assert.equal(cur_frm.doc.paid_amount, 100, 'paid amount set in payment entry');
|
||||||
assert.equal(cur_frm.doc.references[0].allocated_amount, 100,
|
assert.equal(cur_frm.doc.references[0].allocated_amount, 100,
|
||||||
'amount allocated against sales invoice');
|
'amount allocated against sales invoice');
|
||||||
@ -50,10 +50,10 @@ QUnit.test("test payment entry", function(assert) {
|
|||||||
assert.equal(cur_frm.doc.difference_amount, 5, 'difference amount is 5');
|
assert.equal(cur_frm.doc.difference_amount, 5, 'difference amount is 5');
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
frappe.db.set_value("Company", "_Test Company", "write_off_account", "_Test Write Off - _TC");
|
frappe.db.set_value("Company", "For Testing", "write_off_account", "_Test Write Off - FT");
|
||||||
frappe.timeout(1);
|
frappe.timeout(1);
|
||||||
frappe.db.set_value("Company", "_Test Company",
|
frappe.db.set_value("Company", "For Testing",
|
||||||
"exchange_gain_loss_account", "_Test Exchange Gain/Loss - _TC");
|
"exchange_gain_loss_account", "_Test Exchange Gain/Loss - FT");
|
||||||
},
|
},
|
||||||
() => frappe.timeout(1),
|
() => frappe.timeout(1),
|
||||||
() => frappe.click_button('Write Off Difference Amount'),
|
() => frappe.click_button('Write Off Difference Amount'),
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"allow_copy": 0,
|
||||||
|
"allow_guest_to_view": 0,
|
||||||
"allow_import": 0,
|
"allow_import": 0,
|
||||||
"allow_rename": 0,
|
"allow_rename": 0,
|
||||||
"beta": 0,
|
"beta": 0,
|
||||||
@ -12,6 +13,7 @@
|
|||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -42,6 +44,7 @@
|
|||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -72,6 +75,7 @@
|
|||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -101,6 +105,38 @@
|
|||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"depends_on": "",
|
||||||
|
"fieldname": "bill_no",
|
||||||
|
"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": "Supplier Invoice No",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 1,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -129,6 +165,7 @@
|
|||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -158,6 +195,7 @@
|
|||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -187,6 +225,7 @@
|
|||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
@ -216,10 +255,12 @@
|
|||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"depends_on": "eval:(doc.reference_doctype=='Purchase Invoice')",
|
||||||
"fieldname": "exchange_rate",
|
"fieldname": "exchange_rate",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -245,17 +286,17 @@
|
|||||||
"unique": 0
|
"unique": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"has_web_view": 0,
|
||||||
"hide_heading": 0,
|
"hide_heading": 0,
|
||||||
"hide_toolbar": 0,
|
"hide_toolbar": 0,
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"image_view": 0,
|
"image_view": 0,
|
||||||
"in_create": 0,
|
"in_create": 0,
|
||||||
"in_dialog": 0,
|
|
||||||
"is_submittable": 0,
|
"is_submittable": 0,
|
||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2017-02-17 16:47:17.156256",
|
"modified": "2017-09-04 17:37:01.192312",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Entry Reference",
|
"name": "Payment Entry Reference",
|
||||||
|
@ -35,7 +35,6 @@ class PaymentRequest(Document):
|
|||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
send_mail = True
|
send_mail = True
|
||||||
self.make_communication_entry()
|
|
||||||
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
|
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
|
||||||
|
|
||||||
if (hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart") \
|
if (hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart") \
|
||||||
@ -45,6 +44,7 @@ class PaymentRequest(Document):
|
|||||||
if send_mail:
|
if send_mail:
|
||||||
self.set_payment_request_url()
|
self.set_payment_request_url()
|
||||||
self.send_email()
|
self.send_email()
|
||||||
|
self.make_communication_entry()
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.check_if_payment_entry_exists()
|
self.check_if_payment_entry_exists()
|
||||||
@ -69,8 +69,11 @@ class PaymentRequest(Document):
|
|||||||
self.db_set('status', 'Initiated')
|
self.db_set('status', 'Initiated')
|
||||||
|
|
||||||
def get_payment_url(self):
|
def get_payment_url(self):
|
||||||
data = frappe.db.get_value(self.reference_doctype, self.reference_name,
|
if self.reference_doctype != "Fees":
|
||||||
["company", "customer_name"], as_dict=1)
|
data = frappe.db.get_value(self.reference_doctype, self.reference_name, ["company", "customer_name"], as_dict=1)
|
||||||
|
else:
|
||||||
|
data = frappe.db.get_value(self.reference_doctype, self.reference_name, ["student_name"], as_dict=1)
|
||||||
|
data.update({"company": frappe.defaults.get_defaults().company})
|
||||||
|
|
||||||
controller = get_payment_gateway_controller(self.payment_gateway)
|
controller = get_payment_gateway_controller(self.payment_gateway)
|
||||||
controller.validate_transaction_currency(self.currency)
|
controller.validate_transaction_currency(self.currency)
|
||||||
@ -277,6 +280,9 @@ def get_amount(ref_doc, dt):
|
|||||||
else:
|
else:
|
||||||
grand_total = flt(ref_doc.outstanding_amount) / ref_doc.conversion_rate
|
grand_total = flt(ref_doc.outstanding_amount) / ref_doc.conversion_rate
|
||||||
|
|
||||||
|
if dt == "Fees":
|
||||||
|
grand_total = ref_doc.outstanding_amount
|
||||||
|
|
||||||
if grand_total > 0 :
|
if grand_total > 0 :
|
||||||
return grand_total
|
return grand_total
|
||||||
|
|
||||||
|
@ -2072,6 +2072,37 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "base_rounding_adjustment",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"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": "Rounding Adjustment (Company Currency)",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 1,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
@ -2166,6 +2197,37 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "rounding_adjustment",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"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": "Rounding Adjustment",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "currency",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 1,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
|
@ -15,6 +15,7 @@ from erpnext.stock import get_warehouse_account_map
|
|||||||
from erpnext.accounts.general_ledger import make_gl_entries, merge_similar_entries, delete_gl_entries
|
from erpnext.accounts.general_ledger import make_gl_entries, merge_similar_entries, delete_gl_entries
|
||||||
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
|
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
|
||||||
from erpnext.buying.utils import check_for_closed_status
|
from erpnext.buying.utils import check_for_closed_status
|
||||||
|
from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center
|
||||||
|
|
||||||
form_grid_templates = {
|
form_grid_templates = {
|
||||||
"items": "templates/form_grid/item_grid.html"
|
"items": "templates/form_grid/item_grid.html"
|
||||||
@ -353,6 +354,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
|
|
||||||
self.make_payment_gl_entries(gl_entries)
|
self.make_payment_gl_entries(gl_entries)
|
||||||
self.make_write_off_gl_entry(gl_entries)
|
self.make_write_off_gl_entry(gl_entries)
|
||||||
|
self.make_gle_for_rounding_adjustment(gl_entries)
|
||||||
|
|
||||||
return gl_entries
|
return gl_entries
|
||||||
|
|
||||||
@ -604,6 +606,21 @@ class PurchaseInvoice(BuyingController):
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def make_gle_for_rounding_adjustment(self, gl_entries):
|
||||||
|
if self.rounding_adjustment:
|
||||||
|
round_off_account, round_off_cost_center = \
|
||||||
|
get_round_off_account_and_cost_center(self.company)
|
||||||
|
|
||||||
|
gl_entries.append(
|
||||||
|
self.get_gl_dict({
|
||||||
|
"account": round_off_account,
|
||||||
|
"against": self.supplier,
|
||||||
|
"debit_in_account_currency": self.rounding_adjustment,
|
||||||
|
"debit": self.base_rounding_adjustment,
|
||||||
|
"cost_center": round_off_cost_center,
|
||||||
|
}
|
||||||
|
))
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.check_for_closed_status()
|
self.check_for_closed_status()
|
||||||
|
|
||||||
|
@ -1670,36 +1670,6 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "net_total",
|
|
||||||
"fieldtype": "Currency",
|
|
||||||
"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": "Net Total",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "currency",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 1,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
@ -1731,6 +1701,36 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "net_total",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"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": "Net Total",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "currency",
|
||||||
|
"permlevel": 0,
|
||||||
|
"print_hide": 1,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
@ -2337,6 +2337,37 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "base_rounding_adjustment",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"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": "Rounding Adjustment (Company Currency)",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 1,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
@ -2463,6 +2494,37 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "rounding_adjustment",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"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": "Rounding Adjustment",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "currency",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 1,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
|
@ -20,6 +20,7 @@ from erpnext.accounts.doctype.asset.depreciation \
|
|||||||
from erpnext.stock.doctype.batch.batch import set_batch_nos
|
from erpnext.stock.doctype.batch.batch import set_batch_nos
|
||||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos, get_delivery_note_serial_no
|
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos, get_delivery_note_serial_no
|
||||||
from erpnext.setup.doctype.company.company import update_company_current_month_sales
|
from erpnext.setup.doctype.company.company import update_company_current_month_sales
|
||||||
|
from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center
|
||||||
|
|
||||||
form_grid_templates = {
|
form_grid_templates = {
|
||||||
"items": "templates/form_grid/item_grid.html"
|
"items": "templates/form_grid/item_grid.html"
|
||||||
@ -625,6 +626,7 @@ class SalesInvoice(SellingController):
|
|||||||
self.make_gle_for_change_amount(gl_entries)
|
self.make_gle_for_change_amount(gl_entries)
|
||||||
|
|
||||||
self.make_write_off_gl_entry(gl_entries)
|
self.make_write_off_gl_entry(gl_entries)
|
||||||
|
self.make_gle_for_rounding_adjustment(gl_entries)
|
||||||
|
|
||||||
return gl_entries
|
return gl_entries
|
||||||
|
|
||||||
@ -804,6 +806,21 @@ class SalesInvoice(SellingController):
|
|||||||
}, write_off_account_currency)
|
}, write_off_account_currency)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def make_gle_for_rounding_adjustment(self, gl_entries):
|
||||||
|
if self.rounding_adjustment:
|
||||||
|
round_off_account, round_off_cost_center = \
|
||||||
|
get_round_off_account_and_cost_center(self.company)
|
||||||
|
|
||||||
|
gl_entries.append(
|
||||||
|
self.get_gl_dict({
|
||||||
|
"account": round_off_account,
|
||||||
|
"against": self.customer,
|
||||||
|
"credit_in_account_currency": self.rounding_adjustment,
|
||||||
|
"credit": self.base_rounding_adjustment,
|
||||||
|
"cost_center": round_off_cost_center,
|
||||||
|
}
|
||||||
|
))
|
||||||
|
|
||||||
def update_billing_status_in_dn(self, update_modified=True):
|
def update_billing_status_in_dn(self, update_modified=True):
|
||||||
updated_delivery_notes = []
|
updated_delivery_notes = []
|
||||||
for d in self.get("items"):
|
for d in self.get("items"):
|
||||||
|
@ -224,12 +224,12 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
si.save()
|
si.save()
|
||||||
|
|
||||||
# with inclusive tax and additional discount
|
# with inclusive tax and additional discount
|
||||||
self.assertEquals(si.net_total, 4298.24)
|
self.assertEquals(si.net_total, 4298.25)
|
||||||
self.assertEquals(si.grand_total, 4900.00)
|
self.assertEquals(si.grand_total, 4900.00)
|
||||||
|
|
||||||
def test_sales_invoice_discount_amount(self):
|
def test_sales_invoice_discount_amount(self):
|
||||||
si = frappe.copy_doc(test_records[3])
|
si = frappe.copy_doc(test_records[3])
|
||||||
si.discount_amount = 104.95
|
si.discount_amount = 104.94
|
||||||
si.append("taxes", {
|
si.append("taxes", {
|
||||||
"charge_type": "On Previous Row Amount",
|
"charge_type": "On Previous Row Amount",
|
||||||
"account_head": "_Test Account Service Tax - _TC",
|
"account_head": "_Test Account Service Tax - _TC",
|
||||||
@ -294,7 +294,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
"_Test Account Customs Duty - _TC": [125, 116.35, 1585.40],
|
"_Test Account Customs Duty - _TC": [125, 116.35, 1585.40],
|
||||||
"_Test Account Shipping Charges - _TC": [100, 100, 1685.40],
|
"_Test Account Shipping Charges - _TC": [100, 100, 1685.40],
|
||||||
"_Test Account Discount - _TC": [-180.33, -168.54, 1516.86],
|
"_Test Account Discount - _TC": [-180.33, -168.54, 1516.86],
|
||||||
"_Test Account Service Tax - _TC": [-18.03, -16.86, 1500]
|
"_Test Account Service Tax - _TC": [-18.03, -16.85, 1500.01]
|
||||||
}
|
}
|
||||||
|
|
||||||
for d in si.get("taxes"):
|
for d in si.get("taxes"):
|
||||||
@ -303,10 +303,12 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEquals(si.base_grand_total, 1500)
|
self.assertEquals(si.base_grand_total, 1500)
|
||||||
self.assertEquals(si.grand_total, 1500)
|
self.assertEquals(si.grand_total, 1500)
|
||||||
|
self.assertEquals(si.rounding_adjustment, -0.01)
|
||||||
|
|
||||||
def test_discount_amount_gl_entry(self):
|
def test_discount_amount_gl_entry(self):
|
||||||
|
frappe.db.set_value("Company", "_Test Company", "round_off_account", "Round Off - _TC")
|
||||||
si = frappe.copy_doc(test_records[3])
|
si = frappe.copy_doc(test_records[3])
|
||||||
si.discount_amount = 104.95
|
si.discount_amount = 104.94
|
||||||
si.append("taxes", {
|
si.append("taxes", {
|
||||||
"doctype": "Sales Taxes and Charges",
|
"doctype": "Sales Taxes and Charges",
|
||||||
"charge_type": "On Previous Row Amount",
|
"charge_type": "On Previous Row Amount",
|
||||||
@ -336,7 +338,8 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
[test_records[3]["taxes"][5]["account_head"], 0.0, 116.35],
|
[test_records[3]["taxes"][5]["account_head"], 0.0, 116.35],
|
||||||
[test_records[3]["taxes"][6]["account_head"], 0.0, 100],
|
[test_records[3]["taxes"][6]["account_head"], 0.0, 100],
|
||||||
[test_records[3]["taxes"][7]["account_head"], 168.54, 0.0],
|
[test_records[3]["taxes"][7]["account_head"], 168.54, 0.0],
|
||||||
["_Test Account Service Tax - _TC", 16.86, 0.0]
|
["_Test Account Service Tax - _TC", 16.85, 0.0],
|
||||||
|
["Round Off - _TC", 0.01, 0.0]
|
||||||
])
|
])
|
||||||
|
|
||||||
for gle in gl_entries:
|
for gle in gl_entries:
|
||||||
@ -432,13 +435,12 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
expected_values = {
|
expected_values = {
|
||||||
"keys": ["price_list_rate", "discount_percentage", "rate", "amount",
|
"keys": ["price_list_rate", "discount_percentage", "rate", "amount",
|
||||||
"base_price_list_rate", "base_rate", "base_amount", "net_rate", "net_amount"],
|
"base_price_list_rate", "base_rate", "base_amount", "net_rate", "net_amount"],
|
||||||
"_Test Item Home Desktop 100": [62.5, 0, 62.5, 625.0, 62.5, 62.5, 625.0, 50, 499.98],
|
"_Test Item Home Desktop 100": [62.5, 0, 62.5, 625.0, 62.5, 62.5, 625.0, 50, 499.97600115194473],
|
||||||
"_Test Item Home Desktop 200": [190.66, 0, 190.66, 953.3, 190.66, 190.66, 953.3, 150, 750],
|
"_Test Item Home Desktop 200": [190.66, 0, 190.66, 953.3, 190.66, 190.66, 953.3, 150, 749.9968530500239],
|
||||||
}
|
}
|
||||||
|
|
||||||
# check if children are saved
|
# check if children are saved
|
||||||
self.assertEquals(len(si.get("items")),
|
self.assertEquals(len(si.get("items")), len(expected_values)-1)
|
||||||
len(expected_values)-1)
|
|
||||||
|
|
||||||
# check if item values are calculated
|
# check if item values are calculated
|
||||||
for d in si.get("items"):
|
for d in si.get("items"):
|
||||||
@ -446,28 +448,28 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
self.assertEquals(d.get(k), expected_values[d.item_code][i])
|
self.assertEquals(d.get(k), expected_values[d.item_code][i])
|
||||||
|
|
||||||
# check net total
|
# check net total
|
||||||
self.assertEquals(si.base_net_total, 1249.98)
|
self.assertEquals(si.net_total, 1249.97)
|
||||||
self.assertEquals(si.total, 1578.3)
|
self.assertEquals(si.total, 1578.3)
|
||||||
|
|
||||||
# check tax calculation
|
# check tax calculation
|
||||||
expected_values = {
|
expected_values = {
|
||||||
"keys": ["tax_amount", "total"],
|
"keys": ["tax_amount", "total"],
|
||||||
"_Test Account Excise Duty - _TC": [140, 1389.98],
|
"_Test Account Excise Duty - _TC": [140, 1389.97],
|
||||||
"_Test Account Education Cess - _TC": [2.8, 1392.78],
|
"_Test Account Education Cess - _TC": [2.8, 1392.77],
|
||||||
"_Test Account S&H Education Cess - _TC": [1.4, 1394.18],
|
"_Test Account S&H Education Cess - _TC": [1.4, 1394.17],
|
||||||
"_Test Account CST - _TC": [27.88, 1422.06],
|
"_Test Account CST - _TC": [27.88, 1422.05],
|
||||||
"_Test Account VAT - _TC": [156.25, 1578.31],
|
"_Test Account VAT - _TC": [156.25, 1578.30],
|
||||||
"_Test Account Customs Duty - _TC": [125, 1703.31],
|
"_Test Account Customs Duty - _TC": [125, 1703.30],
|
||||||
"_Test Account Shipping Charges - _TC": [100, 1803.31],
|
"_Test Account Shipping Charges - _TC": [100, 1803.30],
|
||||||
"_Test Account Discount - _TC": [-180.33, 1622.98]
|
"_Test Account Discount - _TC": [-180.33, 1622.97]
|
||||||
}
|
}
|
||||||
|
|
||||||
for d in si.get("taxes"):
|
for d in si.get("taxes"):
|
||||||
for i, k in enumerate(expected_values["keys"]):
|
for i, k in enumerate(expected_values["keys"]):
|
||||||
self.assertEquals(d.get(k), expected_values[d.account_head][i])
|
self.assertEquals(d.get(k), expected_values[d.account_head][i])
|
||||||
|
|
||||||
self.assertEquals(si.base_grand_total, 1622.98)
|
self.assertEquals(si.base_grand_total, 1622.97)
|
||||||
self.assertEquals(si.grand_total, 1622.98)
|
self.assertEquals(si.grand_total, 1622.97)
|
||||||
|
|
||||||
def test_sales_invoice_calculation_export_currency_with_tax_inclusive_price(self):
|
def test_sales_invoice_calculation_export_currency_with_tax_inclusive_price(self):
|
||||||
# prepare
|
# prepare
|
||||||
@ -495,7 +497,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
"base_rate": 2500,
|
"base_rate": 2500,
|
||||||
"base_amount": 25000,
|
"base_amount": 25000,
|
||||||
"net_rate": 40,
|
"net_rate": 40,
|
||||||
"net_amount": 399.98,
|
"net_amount": 399.9808009215558,
|
||||||
"base_net_rate": 2000,
|
"base_net_rate": 2000,
|
||||||
"base_net_amount": 19999
|
"base_net_amount": 19999
|
||||||
},
|
},
|
||||||
@ -509,7 +511,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
"base_rate": 7500,
|
"base_rate": 7500,
|
||||||
"base_amount": 37500,
|
"base_amount": 37500,
|
||||||
"net_rate": 118.01,
|
"net_rate": 118.01,
|
||||||
"net_amount": 590.05,
|
"net_amount": 590.0531205155963,
|
||||||
"base_net_rate": 5900.5,
|
"base_net_rate": 5900.5,
|
||||||
"base_net_amount": 29502.5
|
"base_net_amount": 29502.5
|
||||||
}
|
}
|
||||||
@ -545,8 +547,11 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
for i, k in enumerate(expected_values["keys"]):
|
for i, k in enumerate(expected_values["keys"]):
|
||||||
self.assertEquals(d.get(k), expected_values[d.account_head][i])
|
self.assertEquals(d.get(k), expected_values[d.account_head][i])
|
||||||
|
|
||||||
self.assertEquals(si.base_grand_total, 60794.5)
|
self.assertEquals(si.base_grand_total, 60795)
|
||||||
self.assertEquals(si.grand_total, 1215.89)
|
self.assertEquals(si.grand_total, 1215.90)
|
||||||
|
self.assertEquals(si.rounding_adjustment, 0.01)
|
||||||
|
self.assertEquals(si.base_rounding_adjustment, 0.50)
|
||||||
|
|
||||||
|
|
||||||
def test_outstanding(self):
|
def test_outstanding(self):
|
||||||
w = self.make()
|
w = self.make()
|
||||||
@ -1313,6 +1318,40 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
current_month_sales = frappe.db.get_value("Company", "_Test Company", "total_monthly_sales")
|
current_month_sales = frappe.db.get_value("Company", "_Test Company", "total_monthly_sales")
|
||||||
self.assertEqual(current_month_sales, existing_current_month_sales)
|
self.assertEqual(current_month_sales, existing_current_month_sales)
|
||||||
|
|
||||||
|
def test_rounding_adjustment(self):
|
||||||
|
si = create_sales_invoice(rate=24900, do_not_save=True)
|
||||||
|
for tax in ["Tax 1", "Tax2"]:
|
||||||
|
si.append("taxes", {
|
||||||
|
"charge_type": "On Net Total",
|
||||||
|
"account_head": "_Test Account Service Tax - _TC",
|
||||||
|
"description": tax,
|
||||||
|
"rate": 14,
|
||||||
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
|
"included_in_print_rate": 1
|
||||||
|
})
|
||||||
|
si.save()
|
||||||
|
|
||||||
|
self.assertEqual(si.net_total, 19453.13)
|
||||||
|
self.assertEqual(si.grand_total, 24900)
|
||||||
|
self.assertEqual(si.total_taxes_and_charges, 5446.88)
|
||||||
|
self.assertEqual(si.rounding_adjustment, -0.01)
|
||||||
|
|
||||||
|
expected_values = dict((d[0], d) for d in [
|
||||||
|
[si.debit_to, 24900, 0.0],
|
||||||
|
["_Test Account Service Tax - _TC", 0.0, 5446.88],
|
||||||
|
["Sales - _TC", 0.0, 19453.13],
|
||||||
|
["Round Off - _TC", 0.01, 0.0]
|
||||||
|
])
|
||||||
|
|
||||||
|
gl_entries = frappe.db.sql("""select account, debit, credit
|
||||||
|
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
|
||||||
|
order by account asc""", si.name, as_dict=1)
|
||||||
|
|
||||||
|
for gle in gl_entries:
|
||||||
|
self.assertEquals(expected_values[gle.account][0], gle.account)
|
||||||
|
self.assertEquals(expected_values[gle.account][1], gle.debit)
|
||||||
|
self.assertEquals(expected_values[gle.account][2], gle.credit)
|
||||||
|
|
||||||
def create_sales_invoice(**args):
|
def create_sales_invoice(**args):
|
||||||
si = frappe.new_doc("Sales Invoice")
|
si = frappe.new_doc("Sales Invoice")
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
77
erpnext/accounts/doctype/subscription/subscription.js
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on('Subscription', {
|
||||||
|
setup: function(frm) {
|
||||||
|
if(frm.doc.__islocal) {
|
||||||
|
var last_route = frappe.route_history.slice(-2, -1)[0];
|
||||||
|
if(frappe.dynamic_link && frappe.dynamic_link.doc
|
||||||
|
&& frappe.dynamic_link.doc.name==last_route[2]) {
|
||||||
|
frm.set_value('reference_doctype', last_route[1]);
|
||||||
|
frm.set_value('reference_document', last_route[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
frm.fields_dict['reference_document'].get_query = function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
"docstatus": 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
frm.fields_dict['print_format'].get_query = function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
"doc_type": frm.doc.reference_doctype
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
refresh: function(frm) {
|
||||||
|
if(frm.doc.docstatus == 1) {
|
||||||
|
let label = __('View {0}', [frm.doc.reference_doctype]);
|
||||||
|
frm.add_custom_button(__(label),
|
||||||
|
function() {
|
||||||
|
frappe.route_options = {
|
||||||
|
"subscription": frm.doc.name,
|
||||||
|
};
|
||||||
|
frappe.set_route("List", frm.doc.reference_doctype);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if(frm.doc.status != 'Stopped') {
|
||||||
|
frm.add_custom_button(__("Stop"),
|
||||||
|
function() {
|
||||||
|
frm.events.stop_resume_subscription(frm, "Stopped");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(frm.doc.status == 'Stopped') {
|
||||||
|
frm.add_custom_button(__("Resume"),
|
||||||
|
function() {
|
||||||
|
frm.events.stop_resume_subscription(frm, "Resumed");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
stop_resume_subscription: function(frm, status) {
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.accounts.doctype.subscription.subscription.stop_resume_subscription",
|
||||||
|
args: {
|
||||||
|
subscription: frm.doc.name,
|
||||||
|
status: status
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
if(r.message) {
|
||||||
|
frm.set_value("status", r.message);
|
||||||
|
frm.reload_doc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
@ -148,7 +148,7 @@
|
|||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_global_search": 0,
|
"in_global_search": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
"in_standard_filter": 0,
|
||||||
"label": "Disabled",
|
"label": "Disabled",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
@ -619,24 +619,24 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 1,
|
||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "Draft",
|
"default": "Draft",
|
||||||
"fieldname": "status",
|
"fieldname": "status",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"hidden": 1,
|
"hidden": 0,
|
||||||
"ignore_user_permissions": 0,
|
"ignore_user_permissions": 0,
|
||||||
"ignore_xss_filter": 0,
|
"ignore_xss_filter": 0,
|
||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_global_search": 0,
|
"in_global_search": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
"in_standard_filter": 0,
|
||||||
"label": "Status",
|
"label": "Status",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
"options": "\nDraft\nSubmitted\nCancelled\nCompleted",
|
"options": "\nDraft\nStopped\nSubmitted\nCancelled\nCompleted",
|
||||||
"permlevel": 0,
|
"permlevel": 0,
|
||||||
"precision": "",
|
"precision": "",
|
||||||
"print_hide": 0,
|
"print_hide": 0,
|
||||||
@ -690,9 +690,9 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2017-08-29 15:45:16.157643",
|
"modified": "2017-09-14 12:09:38.471458",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Subscription",
|
"module": "Accounts",
|
||||||
"name": "Subscription",
|
"name": "Subscription",
|
||||||
"name_case": "",
|
"name_case": "",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
@ -700,7 +700,7 @@
|
|||||||
{
|
{
|
||||||
"amend": 0,
|
"amend": 0,
|
||||||
"apply_user_permissions": 0,
|
"apply_user_permissions": 0,
|
||||||
"cancel": 1,
|
"cancel": 0,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
@ -716,6 +716,46 @@
|
|||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 1,
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"amend": 0,
|
||||||
|
"apply_user_permissions": 0,
|
||||||
|
"cancel": 0,
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"if_owner": 0,
|
||||||
|
"import": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Accounts User",
|
||||||
|
"set_user_permissions": 0,
|
||||||
|
"share": 1,
|
||||||
|
"submit": 1,
|
||||||
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"amend": 0,
|
||||||
|
"apply_user_permissions": 0,
|
||||||
|
"cancel": 0,
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"if_owner": 0,
|
||||||
|
"import": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Accounts Manager",
|
||||||
|
"set_user_permissions": 0,
|
||||||
|
"share": 1,
|
||||||
|
"submit": 1,
|
||||||
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
"quick_entry": 0,
|
@ -71,13 +71,16 @@ class Subscription(Document):
|
|||||||
|
|
||||||
doc.db_set('subscription', self.name)
|
doc.db_set('subscription', self.name)
|
||||||
|
|
||||||
def update_status(self):
|
def update_status(self, status=None):
|
||||||
self.status = {
|
self.status = {
|
||||||
'0': 'Draft',
|
'0': 'Draft',
|
||||||
'1': 'Submitted',
|
'1': 'Submitted',
|
||||||
'2': 'Cancelled'
|
'2': 'Cancelled'
|
||||||
}[cstr(self.docstatus or 0)]
|
}[cstr(self.docstatus or 0)]
|
||||||
|
|
||||||
|
if status and status != 'Resumed':
|
||||||
|
self.status = status
|
||||||
|
|
||||||
def get_next_schedule_date(start_date, frequency, repeat_on_day):
|
def get_next_schedule_date(start_date, frequency, repeat_on_day):
|
||||||
mcount = month_map.get(frequency)
|
mcount = month_map.get(frequency)
|
||||||
if mcount:
|
if mcount:
|
||||||
@ -93,11 +96,10 @@ def make_subscription_entry(date=None):
|
|||||||
schedule_date = getdate(data.next_schedule_date)
|
schedule_date = getdate(data.next_schedule_date)
|
||||||
while schedule_date <= getdate(today()):
|
while schedule_date <= getdate(today()):
|
||||||
create_documents(data, schedule_date)
|
create_documents(data, schedule_date)
|
||||||
|
|
||||||
schedule_date = get_next_schedule_date(schedule_date,
|
schedule_date = get_next_schedule_date(schedule_date,
|
||||||
data.frequency, data.repeat_on_day)
|
data.frequency, data.repeat_on_day)
|
||||||
|
|
||||||
if schedule_date:
|
if schedule_date and not frappe.db.get_value('Subscription', data.name, 'disabled'):
|
||||||
frappe.db.set_value('Subscription', data.name, 'next_schedule_date', schedule_date)
|
frappe.db.set_value('Subscription', data.name, 'next_schedule_date', schedule_date)
|
||||||
|
|
||||||
def get_subscription_entries(date):
|
def get_subscription_entries(date):
|
||||||
@ -105,23 +107,29 @@ def get_subscription_entries(date):
|
|||||||
where docstatus = 1 and next_schedule_date <=%s
|
where docstatus = 1 and next_schedule_date <=%s
|
||||||
and reference_document is not null and reference_document != ''
|
and reference_document is not null and reference_document != ''
|
||||||
and next_schedule_date <= ifnull(end_date, '2199-12-31')
|
and next_schedule_date <= ifnull(end_date, '2199-12-31')
|
||||||
and ifnull(disabled, 0) = 0""", (date), as_dict=1)
|
and ifnull(disabled, 0) = 0 and status != 'Stopped' """, (date), as_dict=1)
|
||||||
|
|
||||||
def create_documents(data, schedule_date):
|
def create_documents(data, schedule_date):
|
||||||
try:
|
try:
|
||||||
doc = make_new_document(data, schedule_date)
|
doc = make_new_document(data, schedule_date)
|
||||||
if data.notify_by_email:
|
if data.notify_by_email and data.recipients:
|
||||||
send_notification(doc, data.print_format, data.recipients)
|
print_format = data.print_format or "Standard"
|
||||||
|
send_notification(doc, print_format, data.recipients)
|
||||||
|
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
except Exception:
|
except Exception:
|
||||||
frappe.db.rollback()
|
frappe.db.rollback()
|
||||||
frappe.db.begin()
|
frappe.db.begin()
|
||||||
frappe.log_error(frappe.get_traceback())
|
frappe.log_error(frappe.get_traceback())
|
||||||
|
disabled_subscription(data)
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
if data.reference_document and not frappe.flags.in_test:
|
if data.reference_document and not frappe.flags.in_test:
|
||||||
notify_error_to_user(data)
|
notify_error_to_user(data)
|
||||||
|
|
||||||
|
def disabled_subscription(data):
|
||||||
|
subscription = frappe.get_doc('Subscription', data.name)
|
||||||
|
subscription.db_set('disabled', 1)
|
||||||
|
|
||||||
def notify_error_to_user(data):
|
def notify_error_to_user(data):
|
||||||
party = ''
|
party = ''
|
||||||
party_type = ''
|
party_type = ''
|
||||||
@ -134,7 +142,7 @@ def notify_error_to_user(data):
|
|||||||
if party_type:
|
if party_type:
|
||||||
party = frappe.db.get_value(data.reference_doctype, data.reference_document, party_type)
|
party = frappe.db.get_value(data.reference_doctype, data.reference_document, party_type)
|
||||||
|
|
||||||
notify_errors(data.reference_document, data.reference_doctype, party, data.owner)
|
notify_errors(data.reference_document, data.reference_doctype, party, data.owner, data.name)
|
||||||
|
|
||||||
def make_new_document(args, schedule_date):
|
def make_new_document(args, schedule_date):
|
||||||
doc = frappe.get_doc(args.reference_doctype, args.reference_document)
|
doc = frappe.get_doc(args.reference_doctype, args.reference_document)
|
||||||
@ -168,32 +176,32 @@ def get_next_date(dt, mcount, day=None):
|
|||||||
|
|
||||||
def send_notification(new_rv, print_format='Standard', recipients=None):
|
def send_notification(new_rv, print_format='Standard', recipients=None):
|
||||||
"""Notify concerned persons about recurring document generation"""
|
"""Notify concerned persons about recurring document generation"""
|
||||||
recipients = recipients or new_rv.notification_email_address
|
print_format = print_format
|
||||||
print_format = print_format or new_rv.recurring_print_format
|
|
||||||
|
|
||||||
frappe.sendmail(recipients,
|
frappe.sendmail(recipients,
|
||||||
subject= _("New {0}: #{1}").format(new_rv.doctype, new_rv.name),
|
subject= _("New {0}: #{1}").format(new_rv.doctype, new_rv.name),
|
||||||
message = _("Please find attached {0} #{1}").format(new_rv.doctype, new_rv.name),
|
message = _("Please find attached {0} #{1}").format(new_rv.doctype, new_rv.name),
|
||||||
attachments = [frappe.attach_print(new_rv.doctype, new_rv.name, file_name=new_rv.name, print_format=print_format)])
|
attachments = [frappe.attach_print(new_rv.doctype, new_rv.name, file_name=new_rv.name, print_format=print_format)])
|
||||||
|
|
||||||
def notify_errors(doc, doctype, party, owner):
|
def notify_errors(doc, doctype, party, owner, name):
|
||||||
recipients = get_system_managers(only_name=True)
|
recipients = get_system_managers(only_name=True)
|
||||||
frappe.sendmail(recipients + [frappe.db.get_value("User", owner, "email")],
|
frappe.sendmail(recipients + [frappe.db.get_value("User", owner, "email")],
|
||||||
subject="[Urgent] Error while creating recurring %s for %s" % (doctype, doc),
|
subject=_("[Urgent] Error while creating recurring %s for %s" % (doctype, doc)),
|
||||||
message = frappe.get_template("templates/emails/recurring_document_failed.html").render({
|
message = frappe.get_template("templates/emails/recurring_document_failed.html").render({
|
||||||
"type": doctype,
|
"type": _(doctype),
|
||||||
"name": doc,
|
"name": doc,
|
||||||
"party": party or ""
|
"party": party or "",
|
||||||
|
"subscription": name
|
||||||
}))
|
}))
|
||||||
|
|
||||||
assign_task_to_owner(doc, doctype, "Recurring Invoice Failed", recipients)
|
assign_task_to_owner(name, "Recurring Documents Failed", recipients)
|
||||||
|
|
||||||
def assign_task_to_owner(doc, doctype, msg, users):
|
def assign_task_to_owner(name, msg, users):
|
||||||
for d in users:
|
for d in users:
|
||||||
args = {
|
args = {
|
||||||
|
'doctype' : 'Subscription',
|
||||||
'assign_to' : d,
|
'assign_to' : d,
|
||||||
'doctype' : doctype,
|
'name' : name,
|
||||||
'name' : doc,
|
|
||||||
'description' : msg,
|
'description' : msg,
|
||||||
'priority' : 'High'
|
'priority' : 'High'
|
||||||
}
|
}
|
||||||
@ -205,3 +213,16 @@ def make_subscription(doctype, docname):
|
|||||||
doc.reference_doctype = doctype
|
doc.reference_doctype = doctype
|
||||||
doc.reference_document = docname
|
doc.reference_document = docname
|
||||||
return doc
|
return doc
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def stop_resume_subscription(subscription, status):
|
||||||
|
doc = frappe.get_doc('Subscription', subscription)
|
||||||
|
frappe.msgprint(_("Subscription has been {0}").format(status))
|
||||||
|
if status == 'Resumed':
|
||||||
|
doc.next_schedule_date = get_next_schedule_date(today(),
|
||||||
|
doc.frequency, doc.repeat_on_day)
|
||||||
|
|
||||||
|
doc.update_status(status)
|
||||||
|
doc.save()
|
||||||
|
|
||||||
|
return doc.status
|
@ -1,10 +1,14 @@
|
|||||||
frappe.listview_settings['Subscription'] = {
|
frappe.listview_settings['Subscription'] = {
|
||||||
add_fields: ["next_schedule_date"],
|
add_fields: ["next_schedule_date"],
|
||||||
get_indicator: function(doc) {
|
get_indicator: function(doc) {
|
||||||
if(doc.next_schedule_date >= frappe.datetime.get_today() ) {
|
if(doc.disabled) {
|
||||||
|
return [__("Disabled"), "red"];
|
||||||
|
} else if(doc.next_schedule_date >= frappe.datetime.get_today() && doc.status != 'Stopped') {
|
||||||
return [__("Active"), "green"];
|
return [__("Active"), "green"];
|
||||||
} else if(doc.docstatus === 0) {
|
} else if(doc.docstatus === 0) {
|
||||||
return [__("Draft"), "red", "docstatus,=,0"];
|
return [__("Draft"), "red", "docstatus,=,0"];
|
||||||
|
} else if(doc.status === 'Stopped') {
|
||||||
|
return [__("Stopped"), "red"];
|
||||||
} else {
|
} else {
|
||||||
return [__("Expired"), "darkgrey"];
|
return [__("Expired"), "darkgrey"];
|
||||||
}
|
}
|
@ -10,7 +10,7 @@ from erpnext.accounts.utils import get_fiscal_year
|
|||||||
from erpnext.accounts.report.financial_statements import get_months
|
from erpnext.accounts.report.financial_statements import get_months
|
||||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||||
from erpnext.subscription.doctype.subscription.subscription import make_subscription_entry
|
from erpnext.accounts.doctype.subscription.subscription import make_subscription_entry
|
||||||
|
|
||||||
class TestSubscription(unittest.TestCase):
|
class TestSubscription(unittest.TestCase):
|
||||||
def test_daily_subscription(self):
|
def test_daily_subscription(self):
|
@ -135,7 +135,8 @@ def get_tax_template(posting_date, args):
|
|||||||
for key, value in args.iteritems():
|
for key, value in args.iteritems():
|
||||||
if key=="use_for_shopping_cart":
|
if key=="use_for_shopping_cart":
|
||||||
conditions.append("use_for_shopping_cart = {0}".format(1 if value else 0))
|
conditions.append("use_for_shopping_cart = {0}".format(1 if value else 0))
|
||||||
if key == 'customer_group' and value:
|
if key == 'customer_group':
|
||||||
|
if not value: value = _("All Customer Groups")
|
||||||
customer_group_condition = get_customer_group_condition(value)
|
customer_group_condition = get_customer_group_condition(value)
|
||||||
conditions.append("ifnull({0}, '') in ('', {1})".format(key, customer_group_condition))
|
conditions.append("ifnull({0}, '') in ('', {1})".format(key, customer_group_condition))
|
||||||
else:
|
else:
|
||||||
|
@ -39,7 +39,7 @@ class TestTaxRule(unittest.TestCase):
|
|||||||
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1, from_date = "2015-01-01")
|
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1, from_date = "2015-01-01")
|
||||||
tax_rule1.save()
|
tax_rule1.save()
|
||||||
|
|
||||||
self.assertEquals(get_tax_template("2015-01-01", {"customer_group" : "Commercial"}),
|
self.assertEquals(get_tax_template("2015-01-01", {"customer_group" : "Commercial", "use_for_shopping_cart":0}),
|
||||||
"_Test Sales Taxes and Charges Template")
|
"_Test Sales Taxes and Charges Template")
|
||||||
|
|
||||||
def test_conflict_with_overlapping_dates(self):
|
def test_conflict_with_overlapping_dates(self):
|
||||||
|
@ -137,14 +137,7 @@ def round_off_debit_credit(gl_map):
|
|||||||
make_round_off_gle(gl_map, debit_credit_diff)
|
make_round_off_gle(gl_map, debit_credit_diff)
|
||||||
|
|
||||||
def make_round_off_gle(gl_map, debit_credit_diff):
|
def make_round_off_gle(gl_map, debit_credit_diff):
|
||||||
round_off_account, round_off_cost_center = frappe.db.get_value("Company", gl_map[0].company,
|
round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(gl_map[0].company)
|
||||||
["round_off_account", "round_off_cost_center"]) or [None, None]
|
|
||||||
if not round_off_account:
|
|
||||||
frappe.throw(_("Please mention Round Off Account in Company"))
|
|
||||||
|
|
||||||
if not round_off_cost_center:
|
|
||||||
frappe.throw(_("Please mention Round Off Cost Center in Company"))
|
|
||||||
|
|
||||||
|
|
||||||
round_off_gle = frappe._dict()
|
round_off_gle = frappe._dict()
|
||||||
for k in ["voucher_type", "voucher_no", "company",
|
for k in ["voucher_type", "voucher_no", "company",
|
||||||
@ -166,6 +159,17 @@ def make_round_off_gle(gl_map, debit_credit_diff):
|
|||||||
|
|
||||||
gl_map.append(round_off_gle)
|
gl_map.append(round_off_gle)
|
||||||
|
|
||||||
|
def get_round_off_account_and_cost_center(company):
|
||||||
|
round_off_account, round_off_cost_center = frappe.db.get_value("Company", company,
|
||||||
|
["round_off_account", "round_off_cost_center"]) or [None, None]
|
||||||
|
if not round_off_account:
|
||||||
|
frappe.throw(_("Please mention Round Off Account in Company"))
|
||||||
|
|
||||||
|
if not round_off_cost_center:
|
||||||
|
frappe.throw(_("Please mention Round Off Cost Center in Company"))
|
||||||
|
|
||||||
|
return round_off_account, round_off_cost_center
|
||||||
|
|
||||||
def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
|
def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
|
||||||
adv_adj=False, update_outstanding="Yes"):
|
adv_adj=False, update_outstanding="Yes"):
|
||||||
|
|
||||||
|
@ -269,9 +269,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
|||||||
this.calculate_outstanding_amount();
|
this.calculate_outstanding_amount();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.frm.doc.customer) {
|
this.set_customer_value_in_party_field();
|
||||||
this.party_field.$input.val(this.frm.doc.customer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.frm.doc.write_off_account) {
|
if (!this.frm.doc.write_off_account) {
|
||||||
this.frm.doc.write_off_account = doc.write_off_account
|
this.frm.doc.write_off_account = doc.write_off_account
|
||||||
@ -282,6 +280,12 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
set_customer_value_in_party_field: function() {
|
||||||
|
if (this.frm.doc.customer) {
|
||||||
|
this.party_field.$input.val(this.frm.doc.customer);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
get_invoice_doc: function (si_docs) {
|
get_invoice_doc: function (si_docs) {
|
||||||
var me = this;
|
var me = this;
|
||||||
this.si_docs = this.get_doc_from_localstorage();
|
this.si_docs = this.get_doc_from_localstorage();
|
||||||
@ -695,6 +699,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
|||||||
|
|
||||||
set_focus: function () {
|
set_focus: function () {
|
||||||
if (this.default_customer || this.frm.doc.customer) {
|
if (this.default_customer || this.frm.doc.customer) {
|
||||||
|
this.set_customer_value_in_party_field();
|
||||||
this.serach_item.$input.focus();
|
this.serach_item.$input.focus();
|
||||||
} else {
|
} else {
|
||||||
this.party_field.$input.focus();
|
this.party_field.$input.focus();
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"align_labels_left": 0,
|
"align_labels_right": 0,
|
||||||
"creation": "2017-08-08 12:33:04.773099",
|
"creation": "2017-08-08 12:33:04.773099",
|
||||||
"custom_format": 1,
|
"custom_format": 1,
|
||||||
"disabled": 0,
|
"disabled": 0,
|
||||||
@ -10,7 +10,7 @@
|
|||||||
"html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tfont-family: Monospace;\n\t\tline-height: 200%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 8in;\n\t\t}\n\t}\n</style>\n\n<p class=\"text-center\">\n\t{{ doc.company }}<br>\n\t{% if doc.company_address_display %}\n\t\t{% set company_address = doc.company_address_display.replace(\"\\n\", \" \").replace(\"<br>\", \" \") %}\n\t\t{% if \"GSTIN\" not in company_address %}\n\t\t\t{{ company_address }}\n\t\t\t<b>{{ _(\"GSTIN\") }}:</b>{{ doc.company_gstin }}\n\t\t{% else %}\n\t\t\t{{ company_address.replace(\"GSTIN\", \"<br>GSTIN\") }}\n\t\t{% endif %}\n\t{% endif %}\n\t<br>\n\t<b>{{ doc.select_print_heading or _(\"Invoice\") }}</b><br>\n</p>\n<p>\n\t<b>{{ _(\"Receipt No\") }}:</b> {{ doc.name }}<br>\n\t<b>{{ _(\"Date\") }}:</b> {{ doc.get_formatted(\"posting_date\") }}<br>\n\t{% if doc.grand_total > 50000 %}\n\t\t{% set customer_address = doc.address_display.replace(\"\\n\", \" \").replace(\"<br>\", \" \") %}\n\t\t<b>{{ _(\"Customer\") }}:</b><br>\n\t\t{{ doc.customer_name }}<br>\n\t\t{{ customer_address }}\n\t{% endif %}\n</p>\n\n<hr>\n<table class=\"table table-condensed cart no-border\">\n\t<thead>\n\t\t<tr>\n\t\t\t<th width=\"40%\">{{ _(\"Item\") }}</b></th>\n\t\t\t<th width=\"30%\" class=\"text-right\">{{ _(\"Qty\") }}</th>\n\t\t\t<th width=\"30%\" class=\"text-right\">{{ _(\"Amount\") }}</th>\n\t\t</tr>\n\t</thead>\n\t<tbody>\n\t\t{%- for item in doc.items -%}\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t<br>{{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.gst_hsn_code -%}\n\t\t\t\t\t<br><b>{{ _(\"HSN/SAC\") }}:</b> {{ item.gst_hsn_code }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t<br><b>{{ _(\"Serial No\") }}:</b> {{ item.serial_no }}\n\t\t\t\t{%- endif -%}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">{{ item.qty }}<br>@ {{ item.rate }}</td>\n\t\t\t<td class=\"text-right\">{{ item.get_formatted(\"amount\") }}</td>\n\t\t</tr>\n\t\t{%- endfor -%}\n\t</tbody>\n</table>\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ _(\"Net Total\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"net_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- for row in doc.taxes -%}\n\t\t{%- if not row.included_in_print_rate -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ row.description }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t{%- endfor -%}\n\t\t{%- if doc.discount_amount -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Grand Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t</tbody>\n</table>\n<p><b>Tax Breakup:</b></p>\n<div style=\"font-size: 8px\">\n\t{{ doc.other_charges_calculation }}\n</div>\n<p>{{ doc.terms or \"\" }}</p>\n<p class=\"text-center\">{{ _(\"Thank you, please visit again.\") }}</p>",
|
"html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tfont-family: Monospace;\n\t\tline-height: 200%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 8in;\n\t\t}\n\t}\n</style>\n\n<p class=\"text-center\">\n\t{{ doc.company }}<br>\n\t{% if doc.company_address_display %}\n\t\t{% set company_address = doc.company_address_display.replace(\"\\n\", \" \").replace(\"<br>\", \" \") %}\n\t\t{% if \"GSTIN\" not in company_address %}\n\t\t\t{{ company_address }}\n\t\t\t<b>{{ _(\"GSTIN\") }}:</b>{{ doc.company_gstin }}\n\t\t{% else %}\n\t\t\t{{ company_address.replace(\"GSTIN\", \"<br>GSTIN\") }}\n\t\t{% endif %}\n\t{% endif %}\n\t<br>\n\t<b>{{ doc.select_print_heading or _(\"Invoice\") }}</b><br>\n</p>\n<p>\n\t<b>{{ _(\"Receipt No\") }}:</b> {{ doc.name }}<br>\n\t<b>{{ _(\"Date\") }}:</b> {{ doc.get_formatted(\"posting_date\") }}<br>\n\t{% if doc.grand_total > 50000 %}\n\t\t{% set customer_address = doc.address_display.replace(\"\\n\", \" \").replace(\"<br>\", \" \") %}\n\t\t<b>{{ _(\"Customer\") }}:</b><br>\n\t\t{{ doc.customer_name }}<br>\n\t\t{{ customer_address }}\n\t{% endif %}\n</p>\n\n<hr>\n<table class=\"table table-condensed cart no-border\">\n\t<thead>\n\t\t<tr>\n\t\t\t<th width=\"40%\">{{ _(\"Item\") }}</b></th>\n\t\t\t<th width=\"30%\" class=\"text-right\">{{ _(\"Qty\") }}</th>\n\t\t\t<th width=\"30%\" class=\"text-right\">{{ _(\"Amount\") }}</th>\n\t\t</tr>\n\t</thead>\n\t<tbody>\n\t\t{%- for item in doc.items -%}\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t<br>{{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.gst_hsn_code -%}\n\t\t\t\t\t<br><b>{{ _(\"HSN/SAC\") }}:</b> {{ item.gst_hsn_code }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t<br><b>{{ _(\"Serial No\") }}:</b> {{ item.serial_no }}\n\t\t\t\t{%- endif -%}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">{{ item.qty }}<br>@ {{ item.rate }}</td>\n\t\t\t<td class=\"text-right\">{{ item.get_formatted(\"amount\") }}</td>\n\t\t</tr>\n\t\t{%- endfor -%}\n\t</tbody>\n</table>\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ _(\"Net Total\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"net_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- for row in doc.taxes -%}\n\t\t{%- if not row.included_in_print_rate -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ row.description }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t{%- endfor -%}\n\t\t{%- if doc.discount_amount -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Grand Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t</tbody>\n</table>\n<p><b>Tax Breakup:</b></p>\n<div style=\"font-size: 8px\">\n\t{{ doc.other_charges_calculation }}\n</div>\n<p>{{ doc.terms or \"\" }}</p>\n<p class=\"text-center\">{{ _(\"Thank you, please visit again.\") }}</p>",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"line_breaks": 0,
|
"line_breaks": 0,
|
||||||
"modified": "2017-08-29 15:54:19.467642",
|
"modified": "2017-09-14 15:54:19.467642",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "GST POS Invoice",
|
"name": "GST POS Invoice",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"align_labels_left": 0,
|
"align_labels_right": 0,
|
||||||
"creation": "2016-05-05 17:16:18.564460",
|
"creation": "2016-05-05 17:16:18.564460",
|
||||||
"custom_format": 1,
|
"custom_format": 1,
|
||||||
"disabled": 0,
|
"disabled": 0,
|
||||||
@ -10,7 +10,7 @@
|
|||||||
"html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tfont-family: Monospace;\n\t\tline-height: 200%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 8in;\n\t\t}\n\t}\n</style>\n\n<p class=\"text-center\">\n\t{{ company }}<br>\n\t{{ __(\"POS No : \") }} {{ offline_pos_name }}<br>\n</p>\n<p>\n\t<b>{{ __(\"Customer\") }}:</b> {{ customer }}<br>\n</p>\n\n<p>\n\t<b>{{ __(\"Date\") }}:</b> {{ dateutil.global_date_format(posting_date) }}<br>\n</p>\n\n<hr>\n<table class=\"table table-condensed cart no-border\">\n\t<thead>\n\t\t<tr>\n\t\t\t<th width=\"50%\">{{ __(\"Item\") }}</b></th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ __(\"Qty\") }}</th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ __(\"Amount\") }}</th>\n\t\t</tr>\n\t</thead>\n\t<tbody>\n\t\t{% for item in items %}\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t{{ item.item_name }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">{{ format_number(item.qty, null,precision(\"difference\")) }}<br>@ {{ format_currency(item.rate, currency) }}</td>\n\t\t\t<td class=\"text-right\">{{ format_currency(item.amount, currency) }}</td>\n\t\t</tr>\n\t\t{% endfor %}\n\t</tbody>\n</table>\n\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ __(\"Net Total\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(total, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{% for row in taxes %}\n\t\t{% if not row.included_in_print_rate %}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ row.description }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(row.tax_amount, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{% endif %}\n\t\t{% endfor %}\n\t\t{% if discount_amount %}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t{{ __(\"Discount\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(discount_amount, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{% endif %}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ __(\"Grand Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(grand_total, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ __(\"Paid Amount\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(paid_amount, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t</tbody>\n</table>\n\n\n<hr>\n<p>{{ terms }}</p>\n<p class=\"text-center\">{{ __(\"Thank you, please visit again.\") }}</p>",
|
"html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tfont-family: Monospace;\n\t\tline-height: 200%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 8in;\n\t\t}\n\t}\n</style>\n\n<p class=\"text-center\">\n\t{{ company }}<br>\n\t{{ __(\"POS No : \") }} {{ offline_pos_name }}<br>\n</p>\n<p>\n\t<b>{{ __(\"Customer\") }}:</b> {{ customer }}<br>\n</p>\n\n<p>\n\t<b>{{ __(\"Date\") }}:</b> {{ dateutil.global_date_format(posting_date) }}<br>\n</p>\n\n<hr>\n<table class=\"table table-condensed cart no-border\">\n\t<thead>\n\t\t<tr>\n\t\t\t<th width=\"50%\">{{ __(\"Item\") }}</b></th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ __(\"Qty\") }}</th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ __(\"Amount\") }}</th>\n\t\t</tr>\n\t</thead>\n\t<tbody>\n\t\t{% for item in items %}\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t{{ item.item_name }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">{{ format_number(item.qty, null,precision(\"difference\")) }}<br>@ {{ format_currency(item.rate, currency) }}</td>\n\t\t\t<td class=\"text-right\">{{ format_currency(item.amount, currency) }}</td>\n\t\t</tr>\n\t\t{% endfor %}\n\t</tbody>\n</table>\n\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ __(\"Net Total\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(total, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{% for row in taxes %}\n\t\t{% if not row.included_in_print_rate %}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ row.description }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(row.tax_amount, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{% endif %}\n\t\t{% endfor %}\n\t\t{% if discount_amount %}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t{{ __(\"Discount\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(discount_amount, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{% endif %}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ __(\"Grand Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(grand_total, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ __(\"Paid Amount\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(paid_amount, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t</tbody>\n</table>\n\n\n<hr>\n<p>{{ terms }}</p>\n<p class=\"text-center\">{{ __(\"Thank you, please visit again.\") }}</p>",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"line_breaks": 0,
|
"line_breaks": 0,
|
||||||
"modified": "2017-09-01 14:27:04.871233",
|
"modified": "2017-09-14 14:36:04.740728",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Point of Sale",
|
"name": "Point of Sale",
|
||||||
|
@ -142,10 +142,16 @@ def get_data(company, root_type, balance_must_be, period_list, filters=None,
|
|||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
def calculate_values(accounts_by_name, gl_entries_by_account, period_list, accumulated_values, ignore_accumulated_values_for_fy):
|
def calculate_values(accounts_by_name, gl_entries_by_account, period_list, accumulated_values, ignore_accumulated_values_for_fy):
|
||||||
for entries in gl_entries_by_account.values():
|
for entries in gl_entries_by_account.values():
|
||||||
for entry in entries:
|
for entry in entries:
|
||||||
d = accounts_by_name.get(entry.account)
|
d = accounts_by_name.get(entry.account)
|
||||||
|
if not d:
|
||||||
|
frappe.msgprint(
|
||||||
|
_("Could not retrieve information for {0}.".format(entry.account)), title="Error",
|
||||||
|
raise_exception=1
|
||||||
|
)
|
||||||
for period in period_list:
|
for period in period_list:
|
||||||
# check if posting date is within the period
|
# check if posting date is within the period
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ def execute(filters=None):
|
|||||||
gross_profit_data = GrossProfitGenerator(filters)
|
gross_profit_data = GrossProfitGenerator(filters)
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
source = gross_profit_data.grouped_data if filters.get("group_by") != "Invoice" else gross_profit_data.data
|
|
||||||
|
|
||||||
group_wise_columns = frappe._dict({
|
group_wise_columns = frappe._dict({
|
||||||
"invoice": ["parent", "customer", "customer_group", "posting_date","item_code", "item_name","item_group", "brand", "description", \
|
"invoice": ["parent", "customer", "customer_group", "posting_date","item_code", "item_name","item_group", "brand", "description", \
|
||||||
@ -45,7 +44,7 @@ def execute(filters=None):
|
|||||||
|
|
||||||
columns = get_columns(group_wise_columns, filters)
|
columns = get_columns(group_wise_columns, filters)
|
||||||
|
|
||||||
for src in source:
|
for src in gross_profit_data.grouped_data:
|
||||||
row = []
|
row = []
|
||||||
for col in group_wise_columns.get(scrub(filters.group_by)):
|
for col in group_wise_columns.get(scrub(filters.group_by)):
|
||||||
row.append(src.get(col))
|
row.append(src.get(col))
|
||||||
@ -103,6 +102,7 @@ class GrossProfitGenerator(object):
|
|||||||
self.load_stock_ledger_entries()
|
self.load_stock_ledger_entries()
|
||||||
self.load_product_bundle()
|
self.load_product_bundle()
|
||||||
self.load_non_stock_items()
|
self.load_non_stock_items()
|
||||||
|
self.get_returned_invoice_items()
|
||||||
self.process()
|
self.process()
|
||||||
|
|
||||||
def process(self):
|
def process(self):
|
||||||
@ -143,20 +143,16 @@ class GrossProfitGenerator(object):
|
|||||||
row.gross_profit_percent = 0.0
|
row.gross_profit_percent = 0.0
|
||||||
|
|
||||||
# add to grouped
|
# add to grouped
|
||||||
if self.filters.group_by != "Invoice":
|
|
||||||
self.grouped.setdefault(row.get(scrub(self.filters.group_by)), []).append(row)
|
self.grouped.setdefault(row.get(scrub(self.filters.group_by)), []).append(row)
|
||||||
|
|
||||||
self.data.append(row)
|
|
||||||
|
|
||||||
if self.grouped:
|
if self.grouped:
|
||||||
self.get_average_rate_based_on_group_by()
|
self.get_average_rate_based_on_group_by()
|
||||||
else:
|
|
||||||
self.grouped_data = []
|
|
||||||
|
|
||||||
def get_average_rate_based_on_group_by(self):
|
def get_average_rate_based_on_group_by(self):
|
||||||
# sum buying / selling totals for group
|
# sum buying / selling totals for group
|
||||||
self.grouped_data = []
|
self.grouped_data = []
|
||||||
for key in self.grouped.keys():
|
for key in self.grouped.keys():
|
||||||
|
if self.filters.get("group_by") != "Invoice":
|
||||||
for i, row in enumerate(self.grouped[key]):
|
for i, row in enumerate(self.grouped[key]):
|
||||||
if i==0:
|
if i==0:
|
||||||
new_row = row
|
new_row = row
|
||||||
@ -164,19 +160,51 @@ class GrossProfitGenerator(object):
|
|||||||
new_row.qty += row.qty
|
new_row.qty += row.qty
|
||||||
new_row.buying_amount += row.buying_amount
|
new_row.buying_amount += row.buying_amount
|
||||||
new_row.base_amount += row.base_amount
|
new_row.base_amount += row.base_amount
|
||||||
|
new_row = self.set_average_rate(new_row)
|
||||||
|
self.grouped_data.append(new_row)
|
||||||
|
else:
|
||||||
|
for i, row in enumerate(self.grouped[key]):
|
||||||
|
if row.parent in self.returned_invoices \
|
||||||
|
and row.item_code in self.returned_invoices[row.parent]:
|
||||||
|
returned_item_rows = self.returned_invoices[row.parent][row.item_code]
|
||||||
|
for returned_item_row in returned_item_rows:
|
||||||
|
row.qty += returned_item_row.qty
|
||||||
|
row.base_amount += returned_item_row.base_amount
|
||||||
|
row.buying_amount = row.qty * row.buying_rate
|
||||||
|
if row.qty:
|
||||||
|
row = self.set_average_rate(row)
|
||||||
|
self.grouped_data.append(row)
|
||||||
|
|
||||||
|
def set_average_rate(self, new_row):
|
||||||
new_row.gross_profit = new_row.base_amount - new_row.buying_amount
|
new_row.gross_profit = new_row.base_amount - new_row.buying_amount
|
||||||
new_row.gross_profit_percent = ((new_row.gross_profit / new_row.base_amount) * 100.0) \
|
new_row.gross_profit_percent = ((new_row.gross_profit / new_row.base_amount) * 100.0) \
|
||||||
if new_row.base_amount else 0
|
if new_row.base_amount else 0
|
||||||
new_row.buying_rate = (new_row.buying_amount / new_row.qty) \
|
new_row.buying_rate = (new_row.buying_amount / new_row.qty) if new_row.qty else 0
|
||||||
if new_row.qty else 0
|
new_row.base_rate = (new_row.base_amount / new_row.qty) if new_row.qty else 0
|
||||||
new_row.base_rate = (new_row.base_amount / new_row.qty) \
|
return new_row
|
||||||
if new_row.qty else 0
|
|
||||||
|
|
||||||
self.grouped_data.append(new_row)
|
def get_returned_invoice_items(self):
|
||||||
|
returned_invoices = frappe.db.sql("""
|
||||||
|
select
|
||||||
|
si.name, si_item.item_code, si_item.qty, si_item.base_amount, si.return_against
|
||||||
|
from
|
||||||
|
`tabSales Invoice` si, `tabSales Invoice Item` si_item
|
||||||
|
where
|
||||||
|
si.name = si_item.parent
|
||||||
|
and si.docstatus = 1
|
||||||
|
and si.is_return = 1
|
||||||
|
""", as_dict=1)
|
||||||
|
|
||||||
|
self.returned_invoices = frappe._dict()
|
||||||
|
for inv in returned_invoices:
|
||||||
|
self.returned_invoices.setdefault(inv.return_against, frappe._dict())\
|
||||||
|
.setdefault(inv.item_code, []).append(inv)
|
||||||
|
|
||||||
def skip_row(self, row, product_bundles):
|
def skip_row(self, row, product_bundles):
|
||||||
if self.filters.get("group_by") != "Invoice" and not row.get(scrub(self.filters.get("group_by"))):
|
if self.filters.get("group_by") != "Invoice":
|
||||||
|
if not row.get(scrub(self.filters.get("group_by"))):
|
||||||
|
return True
|
||||||
|
elif row.get("is_return") == 1:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_buying_amount_from_product_bundle(self, row, product_bundle):
|
def get_buying_amount_from_product_bundle(self, row, product_bundle):
|
||||||
@ -268,17 +296,23 @@ class GrossProfitGenerator(object):
|
|||||||
sales_person_cols = ""
|
sales_person_cols = ""
|
||||||
sales_team_table = ""
|
sales_team_table = ""
|
||||||
|
|
||||||
self.si_list = frappe.db.sql("""select `tabSales Invoice Item`.parenttype, `tabSales Invoice Item`.parent,
|
self.si_list = frappe.db.sql("""
|
||||||
`tabSales Invoice`.posting_date, `tabSales Invoice`.posting_time, `tabSales Invoice`.project, `tabSales Invoice`.update_stock,
|
select
|
||||||
`tabSales Invoice`.customer, `tabSales Invoice`.customer_group, `tabSales Invoice`.territory,
|
`tabSales Invoice Item`.parenttype, `tabSales Invoice Item`.parent,
|
||||||
`tabSales Invoice Item`.item_code, `tabSales Invoice Item`.item_name, `tabSales Invoice Item`.description,
|
`tabSales Invoice`.posting_date, `tabSales Invoice`.posting_time,
|
||||||
`tabSales Invoice Item`.warehouse, `tabSales Invoice Item`.item_group, `tabSales Invoice Item`.brand,
|
`tabSales Invoice`.project, `tabSales Invoice`.update_stock,
|
||||||
`tabSales Invoice Item`.dn_detail, `tabSales Invoice Item`.delivery_note, `tabSales Invoice Item`.stock_qty as qty,
|
`tabSales Invoice`.customer, `tabSales Invoice`.customer_group,
|
||||||
`tabSales Invoice Item`.base_net_rate, `tabSales Invoice Item`.base_net_amount, `tabSales Invoice Item`.name as "item_row"
|
`tabSales Invoice`.territory, `tabSales Invoice Item`.item_code,
|
||||||
|
`tabSales Invoice Item`.item_name, `tabSales Invoice Item`.description,
|
||||||
|
`tabSales Invoice Item`.warehouse, `tabSales Invoice Item`.item_group,
|
||||||
|
`tabSales Invoice Item`.brand, `tabSales Invoice Item`.dn_detail,
|
||||||
|
`tabSales Invoice Item`.delivery_note, `tabSales Invoice Item`.stock_qty as qty,
|
||||||
|
`tabSales Invoice Item`.base_net_rate, `tabSales Invoice Item`.base_net_amount,
|
||||||
|
`tabSales Invoice Item`.name as "item_row", `tabSales Invoice`.is_return
|
||||||
{sales_person_cols}
|
{sales_person_cols}
|
||||||
from
|
from
|
||||||
`tabSales Invoice`
|
`tabSales Invoice` inner join `tabSales Invoice Item`
|
||||||
inner join `tabSales Invoice Item` on `tabSales Invoice Item`.parent = `tabSales Invoice`.name
|
on `tabSales Invoice Item`.parent = `tabSales Invoice`.name
|
||||||
{sales_team_table}
|
{sales_team_table}
|
||||||
where
|
where
|
||||||
`tabSales Invoice`.docstatus=1 {conditions} {match_cond}
|
`tabSales Invoice`.docstatus=1 {conditions} {match_cond}
|
||||||
|
@ -91,10 +91,10 @@ def get_conditions(filters):
|
|||||||
conditions = ""
|
conditions = ""
|
||||||
|
|
||||||
for opts in (("company", " and company=%(company)s"),
|
for opts in (("company", " and company=%(company)s"),
|
||||||
("supplier", " and pi.supplier = %(supplier)s"),
|
("supplier", " and `tabPurchase Invoice`.supplier = %(supplier)s"),
|
||||||
("item_code", " and pi_item.item_code = %(item_code)s"),
|
("item_code", " and `tabPurchase Invoice Item`.item_code = %(item_code)s"),
|
||||||
("from_date", " and pi.posting_date>=%(from_date)s"),
|
("from_date", " and `tabPurchase Invoice`.posting_date>=%(from_date)s"),
|
||||||
("to_date", " and pi.posting_date<=%(to_date)s"),
|
("to_date", " and `tabPurchase Invoice`.posting_date<=%(to_date)s"),
|
||||||
("mode_of_payment", " and ifnull(mode_of_payment, '') = %(mode_of_payment)s")):
|
("mode_of_payment", " and ifnull(mode_of_payment, '') = %(mode_of_payment)s")):
|
||||||
if filters.get(opts[0]):
|
if filters.get(opts[0]):
|
||||||
conditions += opts[1]
|
conditions += opts[1]
|
||||||
@ -104,20 +104,29 @@ def get_conditions(filters):
|
|||||||
def get_items(filters, additional_query_columns):
|
def get_items(filters, additional_query_columns):
|
||||||
conditions = get_conditions(filters)
|
conditions = get_conditions(filters)
|
||||||
match_conditions = frappe.build_match_conditions("Purchase Invoice")
|
match_conditions = frappe.build_match_conditions("Purchase Invoice")
|
||||||
|
|
||||||
|
if match_conditions:
|
||||||
|
match_conditions = " and {0} ".format(match_conditions)
|
||||||
|
|
||||||
if additional_query_columns:
|
if additional_query_columns:
|
||||||
additional_query_columns = ', ' + ', '.join(additional_query_columns)
|
additional_query_columns = ', ' + ', '.join(additional_query_columns)
|
||||||
|
|
||||||
return frappe.db.sql("""
|
return frappe.db.sql("""
|
||||||
select
|
select
|
||||||
pi_item.name, pi_item.parent, pi.posting_date, pi.credit_to, pi.company,
|
`tabPurchase Invoice Item`.`name`, `tabPurchase Invoice Item`.`parent`,
|
||||||
pi.supplier, pi.remarks, pi.base_net_total, pi_item.item_code, pi_item.item_name,
|
`tabPurchase Invoice`.posting_date, `tabPurchase Invoice`.credit_to, `tabPurchase Invoice`.company,
|
||||||
pi_item.item_group, pi_item.project, pi_item.purchase_order, pi_item.purchase_receipt,
|
`tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total, `tabPurchase Invoice Item`.`item_code`,
|
||||||
pi_item.po_detail, pi_item.expense_account, pi_item.stock_qty, pi_item.stock_uom,
|
`tabPurchase Invoice Item`.`item_name`, `tabPurchase Invoice Item`.`item_group`,
|
||||||
pi_item.base_net_rate, pi_item.base_net_amount,
|
`tabPurchase Invoice Item`.`project`, `tabPurchase Invoice Item`.`purchase_order`,
|
||||||
pi.supplier_name, pi.mode_of_payment {0}
|
`tabPurchase Invoice Item`.`purchase_receipt`, `tabPurchase Invoice Item`.`po_detail`,
|
||||||
from `tabPurchase Invoice` pi, `tabPurchase Invoice Item` pi_item
|
`tabPurchase Invoice Item`.`expense_account`, `tabPurchase Invoice Item`.`stock_qty`,
|
||||||
where pi.name = pi_item.parent and pi.docstatus = 1 %s %s
|
`tabPurchase Invoice Item`.`stock_uom`, `tabPurchase Invoice Item`.`base_net_rate`,
|
||||||
order by pi.posting_date desc, pi_item.item_code desc
|
`tabPurchase Invoice Item`.`base_net_amount`,
|
||||||
|
`tabPurchase Invoice`.supplier_name, `tabPurchase Invoice`.mode_of_payment {0}
|
||||||
|
from `tabPurchase Invoice`, `tabPurchase Invoice Item`
|
||||||
|
where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.`parent` and
|
||||||
|
`tabPurchase Invoice`.docstatus = 1 %s %s
|
||||||
|
order by `tabPurchase Invoice`.posting_date desc, `tabPurchase Invoice Item`.item_code desc
|
||||||
""".format(additional_query_columns) % (conditions, match_conditions), filters, as_dict=1)
|
""".format(additional_query_columns) % (conditions, match_conditions), filters, as_dict=1)
|
||||||
|
|
||||||
def get_aii_accounts():
|
def get_aii_accounts():
|
||||||
|
@ -93,10 +93,10 @@ def get_conditions(filters):
|
|||||||
conditions = ""
|
conditions = ""
|
||||||
|
|
||||||
for opts in (("company", " and company=%(company)s"),
|
for opts in (("company", " and company=%(company)s"),
|
||||||
("customer", " and si.customer = %(customer)s"),
|
("customer", " and `tabSales Invoice`.customer = %(customer)s"),
|
||||||
("item_code", " and si_item.item_code = %(item_code)s"),
|
("item_code", " and `tabSales Invoice Item`.item_code = %(item_code)s"),
|
||||||
("from_date", " and si.posting_date>=%(from_date)s"),
|
("from_date", " and `tabSales Invoice`.posting_date>=%(from_date)s"),
|
||||||
("to_date", " and si.posting_date<=%(to_date)s")):
|
("to_date", " and `tabSales Invoice`.posting_date<=%(to_date)s")):
|
||||||
if filters.get(opts[0]):
|
if filters.get(opts[0]):
|
||||||
conditions += opts[1]
|
conditions += opts[1]
|
||||||
|
|
||||||
@ -108,22 +108,34 @@ def get_conditions(filters):
|
|||||||
return conditions
|
return conditions
|
||||||
|
|
||||||
def get_items(filters, additional_query_columns):
|
def get_items(filters, additional_query_columns):
|
||||||
|
conditions = get_conditions(filters)
|
||||||
|
match_conditions = frappe.build_match_conditions("Sales Invoice")
|
||||||
|
|
||||||
|
if match_conditions:
|
||||||
|
match_conditions = " and {0} ".format(match_conditions)
|
||||||
|
|
||||||
if additional_query_columns:
|
if additional_query_columns:
|
||||||
additional_query_columns = ', ' + ', '.join(additional_query_columns)
|
additional_query_columns = ', ' + ', '.join(additional_query_columns)
|
||||||
|
|
||||||
conditions = get_conditions(filters)
|
|
||||||
return frappe.db.sql("""
|
return frappe.db.sql("""
|
||||||
select
|
select
|
||||||
si_item.name, si_item.parent, si.posting_date, si.debit_to, si.project,
|
`tabSales Invoice Item`.name, `tabSales Invoice Item`.parent,
|
||||||
si.customer, si.remarks, si.territory, si.company, si.base_net_total,
|
`tabSales Invoice`.posting_date, `tabSales Invoice`.debit_to,
|
||||||
si_item.item_code, si_item.item_name, si_item.item_group, si_item.sales_order,
|
`tabSales Invoice`.project, `tabSales Invoice`.customer, `tabSales Invoice`.remarks,
|
||||||
si_item.delivery_note, si_item.income_account, si_item.cost_center,
|
`tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total,
|
||||||
si_item.stock_qty, si_item.stock_uom, si_item.base_net_rate, si_item.base_net_amount,
|
`tabSales Invoice Item`.item_code, `tabSales Invoice Item`.item_name,
|
||||||
si.customer_name, si.customer_group, si_item.so_detail, si.update_stock {0}
|
`tabSales Invoice Item`.item_group, `tabSales Invoice Item`.sales_order,
|
||||||
from `tabSales Invoice` si, `tabSales Invoice Item` si_item
|
`tabSales Invoice Item`.delivery_note, `tabSales Invoice Item`.income_account,
|
||||||
where si.name = si_item.parent and si.docstatus = 1 %s
|
`tabSales Invoice Item`.cost_center, `tabSales Invoice Item`.stock_qty,
|
||||||
order by si.posting_date desc, si_item.item_code desc
|
`tabSales Invoice Item`.stock_uom, `tabSales Invoice Item`.base_net_rate,
|
||||||
""".format(additional_query_columns or '') % conditions, filters, as_dict=1)
|
`tabSales Invoice Item`.base_net_amount, `tabSales Invoice`.customer_name,
|
||||||
|
`tabSales Invoice`.customer_group, `tabSales Invoice Item`.so_detail,
|
||||||
|
`tabSales Invoice`.update_stock {0}
|
||||||
|
from `tabSales Invoice`, `tabSales Invoice Item`
|
||||||
|
where `tabSales Invoice`.name = `tabSales Invoice Item`.parent
|
||||||
|
and `tabSales Invoice`.docstatus = 1 %s %s
|
||||||
|
order by `tabSales Invoice`.posting_date desc, `tabSales Invoice Item`.item_code desc
|
||||||
|
""".format(additional_query_columns or '') % (conditions, match_conditions), filters, as_dict=1)
|
||||||
|
|
||||||
def get_delivery_notes_against_sales_order(item_list):
|
def get_delivery_notes_against_sales_order(item_list):
|
||||||
so_dn_map = frappe._dict()
|
so_dn_map = frappe._dict()
|
||||||
|
@ -2102,6 +2102,37 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "base_rounding_adjustment",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"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": "Rounding Adjustment (Company Currency)",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 1,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
@ -2227,6 +2258,37 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "rounding_adjustment",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"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": "Rounding Adjustment",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "currency",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 1,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
|
@ -295,6 +295,7 @@ def make_purchase_receipt(source_name, target_doc=None):
|
|||||||
"field_map": {
|
"field_map": {
|
||||||
"name": "purchase_order_item",
|
"name": "purchase_order_item",
|
||||||
"parent": "purchase_order",
|
"parent": "purchase_order",
|
||||||
|
"bom": "bom"
|
||||||
},
|
},
|
||||||
"postprocess": update_item,
|
"postprocess": update_item,
|
||||||
"condition": lambda doc: abs(doc.received_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1
|
"condition": lambda doc: abs(doc.received_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1
|
||||||
|
@ -12,7 +12,7 @@ QUnit.test("Test: Request for Quotation", function (assert) {
|
|||||||
() => frappe.new_doc("Request for Quotation"),
|
() => frappe.new_doc("Request for Quotation"),
|
||||||
() => frappe.timeout(1),
|
() => frappe.timeout(1),
|
||||||
() => cur_frm.set_value("transaction_date", "04-04-2017"),
|
() => cur_frm.set_value("transaction_date", "04-04-2017"),
|
||||||
() => cur_frm.set_value("company", "_Test Company"),
|
() => cur_frm.set_value("company", "For Testing"),
|
||||||
// Add Suppliers
|
// Add Suppliers
|
||||||
() => {
|
() => {
|
||||||
cur_frm.fields_dict.suppliers.grid.grid_rows[0].toggle_view();
|
cur_frm.fields_dict.suppliers.grid.grid_rows[0].toggle_view();
|
||||||
@ -62,7 +62,7 @@ QUnit.test("Test: Request for Quotation", function (assert) {
|
|||||||
},
|
},
|
||||||
() => frappe.timeout(2),
|
() => frappe.timeout(2),
|
||||||
() => {
|
() => {
|
||||||
cur_frm.fields_dict.items.grid.grid_rows[0].doc.warehouse = "_Test Warehouse - _TC";
|
cur_frm.fields_dict.items.grid.grid_rows[0].doc.warehouse = "_Test Warehouse - FT";
|
||||||
},
|
},
|
||||||
() => frappe.click_button('Save'),
|
() => frappe.click_button('Save'),
|
||||||
() => frappe.timeout(1),
|
() => frappe.timeout(1),
|
||||||
@ -104,7 +104,7 @@ QUnit.test("Test: Request for Quotation", function (assert) {
|
|||||||
() => frappe.timeout(1),
|
() => frappe.timeout(1),
|
||||||
() => frappe.click_button('Make Supplier Quotation'),
|
() => frappe.click_button('Make Supplier Quotation'),
|
||||||
() => frappe.timeout(1),
|
() => frappe.timeout(1),
|
||||||
() => cur_frm.set_value("company", "_Test Company"),
|
() => cur_frm.set_value("company", "For Testing"),
|
||||||
() => cur_frm.fields_dict.items.grid.grid_rows[0].doc.rate = 4.99,
|
() => cur_frm.fields_dict.items.grid.grid_rows[0].doc.rate = 4.99,
|
||||||
() => frappe.timeout(1),
|
() => frappe.timeout(1),
|
||||||
() => frappe.click_button('Save'),
|
() => frappe.click_button('Save'),
|
||||||
|
@ -16,7 +16,7 @@ class Supplier(TransactionBase):
|
|||||||
|
|
||||||
def onload(self):
|
def onload(self):
|
||||||
"""Load address and contacts in `__onload`"""
|
"""Load address and contacts in `__onload`"""
|
||||||
load_address_and_contact(self, "supplier")
|
load_address_and_contact(self)
|
||||||
self.load_dashboard_info()
|
self.load_dashboard_info()
|
||||||
|
|
||||||
def load_dashboard_info(self):
|
def load_dashboard_info(self):
|
||||||
|
@ -13,8 +13,8 @@ QUnit.test("test: supplier", function(assert) {
|
|||||||
{credit_days_based_on: 'Fixed Days'},
|
{credit_days_based_on: 'Fixed Days'},
|
||||||
{accounts: [
|
{accounts: [
|
||||||
[
|
[
|
||||||
{'company': "Test Company"},
|
{'company': "For Testing"},
|
||||||
{'account': "Creditors - TC"}
|
{'account': "Creditors - FT"}
|
||||||
]]
|
]]
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
@ -68,7 +68,7 @@ QUnit.test("test: supplier", function(assert) {
|
|||||||
assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Name correct");
|
assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Name correct");
|
||||||
assert.ok(cur_frm.doc.supplier_type == 'Hardware', "Type correct");
|
assert.ok(cur_frm.doc.supplier_type == 'Hardware', "Type correct");
|
||||||
assert.ok(cur_frm.doc.default_currency == 'INR', "Currency correct");
|
assert.ok(cur_frm.doc.default_currency == 'INR', "Currency correct");
|
||||||
assert.ok(cur_frm.doc.accounts[0].account == 'Creditors - '+frappe.get_abbr('Test Company'), " Account Head abbr correct");
|
assert.ok(cur_frm.doc.accounts[0].account == 'Creditors - '+frappe.get_abbr('For Testing'), " Account Head abbr correct");
|
||||||
assert.ok($('.address-box:nth-child(3) p').text().includes('Shipping City 3'), "Address correct");
|
assert.ok($('.address-box:nth-child(3) p').text().includes('Shipping City 3'), "Address correct");
|
||||||
assert.ok($('.col-sm-6+ .col-sm-6 .h6').text().includes('Contact 3'), "Contact correct");
|
assert.ok($('.col-sm-6+ .col-sm-6 .h6').text().includes('Contact 3'), "Contact correct");
|
||||||
},
|
},
|
||||||
|
@ -1676,6 +1676,37 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "base_rounding_adjustment",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"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": "Rounding Adjustment (Company Currency",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 1,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
@ -1801,6 +1832,37 @@
|
|||||||
"set_only_once": 0,
|
"set_only_once": 0,
|
||||||
"unique": 0
|
"unique": 0
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "rounding_adjustment",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"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": "Rounding Adjustment",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 1,
|
||||||
|
"options": "currency",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 1,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"allow_bulk_edit": 0,
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 0,
|
||||||
|
@ -8,13 +8,13 @@ QUnit.test("test: supplier quotation with item wise discount", function(assert){
|
|||||||
() => {
|
() => {
|
||||||
return frappe.tests.make('Supplier Quotation', [
|
return frappe.tests.make('Supplier Quotation', [
|
||||||
{supplier: 'Test Supplier'},
|
{supplier: 'Test Supplier'},
|
||||||
{company: 'Test Company'},
|
{company: 'For Testing'},
|
||||||
{items: [
|
{items: [
|
||||||
[
|
[
|
||||||
{"item_code": 'Test Product 4'},
|
{"item_code": 'Test Product 4'},
|
||||||
{"qty": 5},
|
{"qty": 5},
|
||||||
{"uom": 'Unit'},
|
{"uom": 'Unit'},
|
||||||
{"warehouse": 'All Warehouses - TC'},
|
{"warehouse": 'All Warehouses - FT'},
|
||||||
{'discount_percentage': 10},
|
{'discount_percentage': 10},
|
||||||
]
|
]
|
||||||
]}
|
]}
|
||||||
|
@ -32,6 +32,12 @@ def get_data():
|
|||||||
"label": _("POS"),
|
"label": _("POS"),
|
||||||
"description": _("Point of Sale")
|
"description": _("Point of Sale")
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Subscription",
|
||||||
|
"label": _("Subscription"),
|
||||||
|
"description": _("To make recurring documents")
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "report",
|
"type": "report",
|
||||||
"name": "Accounts Receivable",
|
"name": "Accounts Receivable",
|
||||||
|
@ -261,5 +261,12 @@ def get_data():
|
|||||||
"icon": "octicon octicon-mortar-board",
|
"icon": "octicon octicon-mortar-board",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"label": _("Schools")
|
"label": _("Schools")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"module_name": "Healthcare",
|
||||||
|
"color": "#FF888B",
|
||||||
|
"icon": "octicon octicon-plus",
|
||||||
|
"type": "module",
|
||||||
|
"label": _("Healthcare")
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
157
erpnext/config/healthcare.py
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
from frappe import _
|
||||||
|
|
||||||
|
def get_data():
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
"label": _("Consultation"),
|
||||||
|
"icon": "icon-star",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Patient Appointment",
|
||||||
|
"description": _("Patient Appointment"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Consultation",
|
||||||
|
"label": _("Consultation"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Vital Signs",
|
||||||
|
"label": _("Vital Signs"),
|
||||||
|
"description": _("Record Patient Vitals"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "page",
|
||||||
|
"name": "medical_record",
|
||||||
|
"label": _("Patient Medical Record"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "page",
|
||||||
|
"name": "appointment-analytic",
|
||||||
|
"label": _("Appointment Analytics"),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Laboratory"),
|
||||||
|
"icon": "icon-list",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Lab Test",
|
||||||
|
"description": _("Results"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Sample Collection",
|
||||||
|
"label": _("Sample Collection"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "report",
|
||||||
|
"name": "Lab Test Report",
|
||||||
|
"is_query_report": True
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Masters"),
|
||||||
|
"icon": "icon-list",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Patient",
|
||||||
|
"label": _("Patient"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Physician",
|
||||||
|
"label": "Physician",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Physician Schedule",
|
||||||
|
"label": _("Physician Schedule"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Medical Code Standard",
|
||||||
|
"label": _("Medical Code Standard"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Medical Code",
|
||||||
|
"label": _("Medical Code"),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": _("Setup"),
|
||||||
|
"icon": "icon-cog",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Healthcare Settings",
|
||||||
|
"label": _("Healthcare Settings"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Medical Department",
|
||||||
|
"label": "Medical Department"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Appointment Type",
|
||||||
|
"description": _("Appointment Type Master"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Prescription Dosage",
|
||||||
|
"description": _("Prescription Dosage")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Prescription Duration",
|
||||||
|
"description": _("Prescription Period")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Complaint",
|
||||||
|
"description": _("Complaint")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Diagnosis",
|
||||||
|
"description": _("Diagnosis")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Lab Test Sample",
|
||||||
|
"description": _("Test Sample Master."),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Lab Test UOM",
|
||||||
|
"description": _("Lab Test UOM.")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Antibiotic",
|
||||||
|
"description": _("Antibiotic.")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Sensitivity",
|
||||||
|
"description": _("Sensitivity Naming.")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Lab Test Template",
|
||||||
|
"description": _("Lab Test Configurations.")
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
@ -154,6 +154,10 @@ def get_data():
|
|||||||
"type": "doctype",
|
"type": "doctype",
|
||||||
"name": "Fees"
|
"name": "Fees"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Fee Schedule"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "doctype",
|
"type": "doctype",
|
||||||
"name": "Fee Structure"
|
"name": "Fee Structure"
|
||||||
|
@ -16,6 +16,8 @@ from erpnext.controllers.stock_controller import StockController
|
|||||||
class BuyingController(StockController):
|
class BuyingController(StockController):
|
||||||
def __setup__(self):
|
def __setup__(self):
|
||||||
if hasattr(self, "taxes"):
|
if hasattr(self, "taxes"):
|
||||||
|
self.flags.print_taxes_with_zero_amount = cint(frappe.db.get_single_value("Print Settings",
|
||||||
|
"print_taxes_with_zero_amount"))
|
||||||
self.print_templates = {
|
self.print_templates = {
|
||||||
"taxes": "templates/print_formats/includes/taxes.html"
|
"taxes": "templates/print_formats/includes/taxes.html"
|
||||||
}
|
}
|
||||||
|
@ -227,14 +227,22 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
"_txt": txt.replace('%', '')
|
"_txt": txt.replace('%', '')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, filters, as_dict):
|
def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, filters, as_dict):
|
||||||
return frappe.db.sql("""
|
return frappe.db.sql("""
|
||||||
select `tabDelivery Note`.name, `tabDelivery Note`.customer, `tabDelivery Note`.posting_date
|
select `tabDelivery Note`.name, `tabDelivery Note`.customer, `tabDelivery Note`.posting_date
|
||||||
from `tabDelivery Note`
|
from `tabDelivery Note`
|
||||||
where `tabDelivery Note`.`%(key)s` like %(txt)s and
|
where `tabDelivery Note`.`%(key)s` like %(txt)s and
|
||||||
`tabDelivery Note`.docstatus = 1 and `tabDelivery Note`.is_return = 0
|
`tabDelivery Note`.docstatus = 1
|
||||||
and status not in ("Stopped", "Closed") %(fcond)s
|
and status not in ("Stopped", "Closed") %(fcond)s
|
||||||
and (`tabDelivery Note`.per_billed < 100 or `tabDelivery Note`.grand_total = 0)
|
and (
|
||||||
|
(`tabDelivery Note`.is_return = 0 and `tabDelivery Note`.per_billed < 100)
|
||||||
|
or `tabDelivery Note`.grand_total = 0
|
||||||
|
or (
|
||||||
|
`tabDelivery Note`.is_return = 1
|
||||||
|
and return_against in (select name from `tabDelivery Note` where per_billed < 100)
|
||||||
|
)
|
||||||
|
)
|
||||||
%(mcond)s order by `tabDelivery Note`.`%(key)s` asc
|
%(mcond)s order by `tabDelivery Note`.`%(key)s` asc
|
||||||
""" % {
|
""" % {
|
||||||
"key": searchfield,
|
"key": searchfield,
|
||||||
@ -243,6 +251,7 @@ def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len,
|
|||||||
"txt": "%(txt)s"
|
"txt": "%(txt)s"
|
||||||
}, {"txt": ("%%%s%%" % txt)}, as_dict=as_dict)
|
}, {"txt": ("%%%s%%" % txt)}, as_dict=as_dict)
|
||||||
|
|
||||||
|
|
||||||
def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
|
def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
|
||||||
cond = ""
|
cond = ""
|
||||||
if filters.get("posting_date"):
|
if filters.get("posting_date"):
|
||||||
|
@ -14,6 +14,8 @@ from erpnext.controllers.stock_controller import StockController
|
|||||||
class SellingController(StockController):
|
class SellingController(StockController):
|
||||||
def __setup__(self):
|
def __setup__(self):
|
||||||
if hasattr(self, "taxes"):
|
if hasattr(self, "taxes"):
|
||||||
|
self.flags.print_taxes_with_zero_amount = cint(frappe.db.get_single_value("Print Settings",
|
||||||
|
"print_taxes_with_zero_amount"))
|
||||||
self.print_templates = {
|
self.print_templates = {
|
||||||
"taxes": "templates/print_formats/includes/taxes.html"
|
"taxes": "templates/print_formats/includes/taxes.html"
|
||||||
}
|
}
|
||||||
@ -177,6 +179,9 @@ class SellingController(StockController):
|
|||||||
return
|
return
|
||||||
|
|
||||||
for it in self.get("items"):
|
for it in self.get("items"):
|
||||||
|
if not it.item_code:
|
||||||
|
continue
|
||||||
|
|
||||||
last_purchase_rate, is_stock_item = frappe.db.get_value("Item", it.item_code, ["last_purchase_rate", "is_stock_item"])
|
last_purchase_rate, is_stock_item = frappe.db.get_value("Item", it.item_code, ["last_purchase_rate", "is_stock_item"])
|
||||||
last_purchase_rate_in_sales_uom = last_purchase_rate / (it.conversion_factor or 1)
|
last_purchase_rate_in_sales_uom = last_purchase_rate / (it.conversion_factor or 1)
|
||||||
if flt(it.base_rate) < flt(last_purchase_rate_in_sales_uom):
|
if flt(it.base_rate) < flt(last_purchase_rate_in_sales_uom):
|
||||||
|
@ -121,9 +121,10 @@ class calculate_taxes_and_totals(object):
|
|||||||
cumulated_tax_fraction += tax.tax_fraction_for_current_item
|
cumulated_tax_fraction += tax.tax_fraction_for_current_item
|
||||||
|
|
||||||
if cumulated_tax_fraction and not self.discount_amount_applied and item.qty:
|
if cumulated_tax_fraction and not self.discount_amount_applied and item.qty:
|
||||||
item.net_amount = flt(item.amount / (1 + cumulated_tax_fraction), item.precision("net_amount"))
|
item.net_amount = flt(item.amount / (1 + cumulated_tax_fraction))
|
||||||
item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
|
item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
|
||||||
item.discount_percentage = flt(item.discount_percentage, item.precision("discount_percentage"))
|
item.discount_percentage = flt(item.discount_percentage,
|
||||||
|
item.precision("discount_percentage"))
|
||||||
|
|
||||||
self._set_in_company_currency(item, ["net_rate", "net_amount"])
|
self._set_in_company_currency(item, ["net_rate", "net_amount"])
|
||||||
|
|
||||||
@ -173,6 +174,7 @@ class calculate_taxes_and_totals(object):
|
|||||||
self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
|
self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
|
||||||
|
|
||||||
def calculate_taxes(self):
|
def calculate_taxes(self):
|
||||||
|
self.doc.rounding_adjustment = 0
|
||||||
# maintain actual tax rate based on idx
|
# maintain actual tax rate based on idx
|
||||||
actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
|
actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
|
||||||
for tax in self.doc.get("taxes") if tax.charge_type == "Actual"])
|
for tax in self.doc.get("taxes") if tax.charge_type == "Actual"])
|
||||||
@ -222,7 +224,9 @@ class calculate_taxes_and_totals(object):
|
|||||||
# adjust Discount Amount loss in last tax iteration
|
# adjust Discount Amount loss in last tax iteration
|
||||||
if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
|
if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
|
||||||
and self.doc.discount_amount and self.doc.apply_discount_on == "Grand Total":
|
and self.doc.discount_amount and self.doc.apply_discount_on == "Grand Total":
|
||||||
self.adjust_discount_amount_loss(tax)
|
self.doc.rounding_adjustment = flt(self.doc.grand_total
|
||||||
|
- flt(self.doc.discount_amount) - tax.total,
|
||||||
|
self.doc.precision("rounding_adjustment"))
|
||||||
|
|
||||||
def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
|
def get_tax_amount_if_for_valuation_or_deduction(self, tax_amount, tax):
|
||||||
# if just for valuation, do not add the tax amount in total
|
# if just for valuation, do not add the tax amount in total
|
||||||
@ -277,36 +281,26 @@ class calculate_taxes_and_totals(object):
|
|||||||
tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount,
|
tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount,
|
||||||
tax.precision("tax_amount"))
|
tax.precision("tax_amount"))
|
||||||
|
|
||||||
def adjust_discount_amount_loss(self, tax):
|
|
||||||
discount_amount_loss = self.doc.grand_total - flt(self.doc.discount_amount) - tax.total
|
|
||||||
tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount +
|
|
||||||
discount_amount_loss, tax.precision("tax_amount"))
|
|
||||||
tax.total = flt(tax.total + discount_amount_loss, tax.precision("total"))
|
|
||||||
|
|
||||||
self._set_in_company_currency(tax, ["total", "tax_amount_after_discount_amount"])
|
|
||||||
|
|
||||||
def manipulate_grand_total_for_inclusive_tax(self):
|
def manipulate_grand_total_for_inclusive_tax(self):
|
||||||
# if fully inclusive taxes and diff
|
# if fully inclusive taxes and diff
|
||||||
if self.doc.get("taxes") and all(cint(t.included_in_print_rate) for t in self.doc.get("taxes")):
|
if self.doc.get("taxes") and any([cint(t.included_in_print_rate) for t in self.doc.get("taxes")]):
|
||||||
last_tax = self.doc.get("taxes")[-1]
|
last_tax = self.doc.get("taxes")[-1]
|
||||||
diff = self.doc.total - flt(last_tax.total, self.doc.precision("grand_total"))
|
non_inclusive_tax_amount = sum([flt(d.tax_amount_after_discount_amount)
|
||||||
|
for d in self.doc.get("taxes") if not d.included_in_print_rate])
|
||||||
if diff and abs(diff) <= (2.0 / 10**last_tax.precision("tax_amount")):
|
diff = self.doc.total + non_inclusive_tax_amount \
|
||||||
last_tax.tax_amount += diff
|
- flt(last_tax.total, last_tax.precision("total"))
|
||||||
last_tax.tax_amount_after_discount_amount += diff
|
if diff and abs(diff) <= (5.0 / 10**last_tax.precision("tax_amount")):
|
||||||
last_tax.total += diff
|
self.doc.rounding_adjustment = flt(flt(self.doc.rounding_adjustment) +
|
||||||
|
flt(diff), self.doc.precision("rounding_adjustment"))
|
||||||
self._set_in_company_currency(last_tax,
|
|
||||||
["total", "tax_amount", "tax_amount_after_discount_amount"])
|
|
||||||
|
|
||||||
def calculate_totals(self):
|
def calculate_totals(self):
|
||||||
self.doc.grand_total = flt(self.doc.get("taxes")[-1].total
|
self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment) \
|
||||||
if self.doc.get("taxes") else self.doc.net_total)
|
if self.doc.get("taxes") else flt(self.doc.net_total)
|
||||||
|
|
||||||
self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total,
|
self.doc.total_taxes_and_charges = flt(self.doc.grand_total - self.doc.net_total
|
||||||
self.doc.precision("total_taxes_and_charges"))
|
- flt(self.doc.rounding_adjustment), self.doc.precision("total_taxes_and_charges"))
|
||||||
|
|
||||||
self._set_in_company_currency(self.doc, ["total_taxes_and_charges"])
|
self._set_in_company_currency(self.doc, ["total_taxes_and_charges", "rounding_adjustment"])
|
||||||
|
|
||||||
if self.doc.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]:
|
if self.doc.doctype in ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]:
|
||||||
self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate) \
|
self.doc.base_grand_total = flt(self.doc.grand_total * self.doc.conversion_rate) \
|
||||||
@ -326,13 +320,22 @@ class calculate_taxes_and_totals(object):
|
|||||||
if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted) \
|
if (self.doc.taxes_and_charges_added or self.doc.taxes_and_charges_deducted) \
|
||||||
else self.doc.base_net_total
|
else self.doc.base_net_total
|
||||||
|
|
||||||
self._set_in_company_currency(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
|
self._set_in_company_currency(self.doc,
|
||||||
|
["taxes_and_charges_added", "taxes_and_charges_deducted"])
|
||||||
|
|
||||||
self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
|
self.doc.round_floats_in(self.doc, ["grand_total", "base_grand_total"])
|
||||||
|
|
||||||
|
self.set_rounded_total()
|
||||||
|
|
||||||
|
def set_rounded_total(self):
|
||||||
|
if frappe.db.get_single_value("Global Defaults", "disable_rounded_total"):
|
||||||
|
self.doc.rounded_total = self.doc.base_rounded_total = 0
|
||||||
|
return
|
||||||
|
|
||||||
if self.doc.meta.get_field("rounded_total"):
|
if self.doc.meta.get_field("rounded_total"):
|
||||||
self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
|
self.doc.rounded_total = round_based_on_smallest_currency_fraction(self.doc.grand_total,
|
||||||
self.doc.currency, self.doc.precision("rounded_total"))
|
self.doc.currency, self.doc.precision("rounded_total"))
|
||||||
|
|
||||||
if self.doc.meta.get_field("base_rounded_total"):
|
if self.doc.meta.get_field("base_rounded_total"):
|
||||||
company_currency = erpnext.get_company_currency(self.doc.company)
|
company_currency = erpnext.get_company_currency(self.doc.company)
|
||||||
|
|
||||||
@ -525,7 +528,7 @@ def get_itemised_tax_breakup_html(doc):
|
|||||||
for tax in doc.taxes:
|
for tax in doc.taxes:
|
||||||
if getattr(tax, "category", None) and tax.category=="Valuation":
|
if getattr(tax, "category", None) and tax.category=="Valuation":
|
||||||
continue
|
continue
|
||||||
if tax.description not in tax_accounts:
|
if tax.description not in tax_accounts and tax.tax_amount_after_discount_amount:
|
||||||
tax_accounts.append(tax.description)
|
tax_accounts.append(tax.description)
|
||||||
|
|
||||||
headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
|
headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
|
||||||
@ -565,17 +568,12 @@ def get_itemised_tax(taxes):
|
|||||||
if getattr(tax, "category", None) and tax.category=="Valuation":
|
if getattr(tax, "category", None) and tax.category=="Valuation":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
tax_amount_precision = tax.precision("tax_amount")
|
|
||||||
tax_rate_precision = tax.precision("rate")
|
|
||||||
|
|
||||||
item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
|
item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
|
||||||
|
if item_tax_map:
|
||||||
for item_code, tax_data in item_tax_map.items():
|
for item_code, tax_data in item_tax_map.items():
|
||||||
itemised_tax.setdefault(item_code, frappe._dict())
|
itemised_tax.setdefault(item_code, frappe._dict())
|
||||||
|
|
||||||
if isinstance(tax_data, list):
|
if isinstance(tax_data, list):
|
||||||
precision = tax_amount_precision if tax.charge_type == "Actual" else tax_rate_precision
|
|
||||||
|
|
||||||
itemised_tax[item_code][tax.description] = frappe._dict(dict(
|
itemised_tax[item_code][tax.description] = frappe._dict(dict(
|
||||||
tax_rate=flt(tax_data[0]),
|
tax_rate=flt(tax_data[0]),
|
||||||
tax_amount=flt(tax_data[1])
|
tax_amount=flt(tax_data[1])
|
||||||
|
@ -19,7 +19,7 @@ QUnit.test("test: item", function (assert) {
|
|||||||
{is_stock_item: is_stock_item},
|
{is_stock_item: is_stock_item},
|
||||||
{standard_rate: keyboard_cost},
|
{standard_rate: keyboard_cost},
|
||||||
{opening_stock: no_of_items_to_stock},
|
{opening_stock: no_of_items_to_stock},
|
||||||
{default_warehouse: "Stores - RB"}
|
{default_warehouse: "Stores - FT"}
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
() => {
|
() => {
|
||||||
@ -45,7 +45,7 @@ QUnit.test("test: item", function (assert) {
|
|||||||
{is_stock_item: is_stock_item},
|
{is_stock_item: is_stock_item},
|
||||||
{standard_rate: screen_cost},
|
{standard_rate: screen_cost},
|
||||||
{opening_stock: no_of_items_to_stock},
|
{opening_stock: no_of_items_to_stock},
|
||||||
{default_warehouse: "Stores - RB"}
|
{default_warehouse: "Stores - FT"}
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ QUnit.test("test: item", function (assert) {
|
|||||||
{is_stock_item: is_stock_item},
|
{is_stock_item: is_stock_item},
|
||||||
{standard_rate: CPU_cost},
|
{standard_rate: CPU_cost},
|
||||||
{opening_stock: no_of_items_to_stock},
|
{opening_stock: no_of_items_to_stock},
|
||||||
{default_warehouse: "Stores - RB"}
|
{default_warehouse: "Stores - FT"}
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ QUnit.test("test: item", function (assert) {
|
|||||||
"Item", [
|
"Item", [
|
||||||
{item_code: "Laptop"},
|
{item_code: "Laptop"},
|
||||||
{item_group: "Products"},
|
{item_group: "Products"},
|
||||||
{default_warehouse: "Stores - RB"}
|
{default_warehouse: "Stores - FT"}
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
() => frappe.tests.make(
|
() => frappe.tests.make(
|
||||||
@ -85,7 +85,7 @@ QUnit.test("test: item", function (assert) {
|
|||||||
{is_stock_item: is_stock_item},
|
{is_stock_item: is_stock_item},
|
||||||
{standard_rate: scrap_cost},
|
{standard_rate: scrap_cost},
|
||||||
{opening_stock: no_of_items_to_stock},
|
{opening_stock: no_of_items_to_stock},
|
||||||
{default_warehouse: "Stores - RB"}
|
{default_warehouse: "Stores - FT"}
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
() => frappe.tests.make(
|
() => frappe.tests.make(
|
||||||
|
@ -20,7 +20,7 @@ class Lead(SellingController):
|
|||||||
def onload(self):
|
def onload(self):
|
||||||
customer = frappe.db.get_value("Customer", {"lead_name": self.name})
|
customer = frappe.db.get_value("Customer", {"lead_name": self.name})
|
||||||
self.get("__onload").is_customer = customer
|
self.get("__onload").is_customer = customer
|
||||||
load_address_and_contact(self, "lead")
|
load_address_and_contact(self)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self._prev = frappe._dict({
|
self._prev = frappe._dict({
|
||||||
|
5420
erpnext/demo/data/drug_list.json
Normal file
@ -167,6 +167,7 @@
|
|||||||
"item_group": "Products",
|
"item_group": "Products",
|
||||||
"item_name": "Wind Turbine-S",
|
"item_name": "Wind Turbine-S",
|
||||||
"variant_of": "Wind Turbine",
|
"variant_of": "Wind Turbine",
|
||||||
|
"valuation_rate": 300,
|
||||||
"attributes":[
|
"attributes":[
|
||||||
{
|
{
|
||||||
"attribute": "Size",
|
"attribute": "Size",
|
||||||
@ -183,6 +184,7 @@
|
|||||||
"item_group": "Products",
|
"item_group": "Products",
|
||||||
"item_name": "Wind Turbine-M",
|
"item_name": "Wind Turbine-M",
|
||||||
"variant_of": "Wind Turbine",
|
"variant_of": "Wind Turbine",
|
||||||
|
"valuation_rate": 300,
|
||||||
"attributes":[
|
"attributes":[
|
||||||
{
|
{
|
||||||
"attribute": "Size",
|
"attribute": "Size",
|
||||||
@ -199,6 +201,7 @@
|
|||||||
"item_group": "Products",
|
"item_group": "Products",
|
||||||
"item_name": "Wind Turbine-L",
|
"item_name": "Wind Turbine-L",
|
||||||
"variant_of": "Wind Turbine",
|
"variant_of": "Wind Turbine",
|
||||||
|
"valuation_rate": 300,
|
||||||
"attributes":[
|
"attributes":[
|
||||||
{
|
{
|
||||||
"attribute": "Size",
|
"attribute": "Size",
|
||||||
|
27
erpnext/demo/data/patient.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"patient_name": "lila",
|
||||||
|
"gender": "Female"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"patient_name": "charline",
|
||||||
|
"gender": "Female"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"patient_name": "soren",
|
||||||
|
"last_name": "le gall",
|
||||||
|
"gender": "Male"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"patient_name": "fanny",
|
||||||
|
"gender": "Female"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"patient_name": "julie",
|
||||||
|
"gender": "Female"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"patient_name": "louka",
|
||||||
|
"gender": "Male"
|
||||||
|
}
|
||||||
|
]
|
17
erpnext/demo/data/physician.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"doctype": "Physician",
|
||||||
|
"first_name": "Eddie Jessup",
|
||||||
|
"department": "Pathology"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"doctype": "Physician",
|
||||||
|
"first_name": "Deepshi Garg",
|
||||||
|
"department": "ENT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"doctype": "Physician",
|
||||||
|
"first_name": "Amit Jain",
|
||||||
|
"department": "Microbiology"
|
||||||
|
}
|
||||||
|
]
|
@ -4,7 +4,7 @@ import frappe, sys
|
|||||||
import erpnext
|
import erpnext
|
||||||
import frappe.utils
|
import frappe.utils
|
||||||
from erpnext.demo.user import hr, sales, purchase, manufacturing, stock, accounts, projects, fixed_asset, schools
|
from erpnext.demo.user import hr, sales, purchase, manufacturing, stock, accounts, projects, fixed_asset, schools
|
||||||
from erpnext.demo.setup import education, manufacture, setup_data
|
from erpnext.demo.setup import education, manufacture, setup_data, healthcare
|
||||||
"""
|
"""
|
||||||
Make a demo
|
Make a demo
|
||||||
|
|
||||||
@ -30,6 +30,8 @@ def make(domain='Manufacturing', days=100):
|
|||||||
manufacture.setup_data()
|
manufacture.setup_data()
|
||||||
elif domain== 'Education':
|
elif domain== 'Education':
|
||||||
education.setup_data()
|
education.setup_data()
|
||||||
|
elif domain== 'Healthcare':
|
||||||
|
healthcare.setup_data()
|
||||||
|
|
||||||
site = frappe.local.site
|
site = frappe.local.site
|
||||||
frappe.destroy()
|
frappe.destroy()
|
||||||
|
@ -15,5 +15,8 @@ data = {
|
|||||||
},
|
},
|
||||||
'Education': {
|
'Education': {
|
||||||
'company_name': 'Whitmore College'
|
'company_name': 'Whitmore College'
|
||||||
|
},
|
||||||
|
'Healthcare': {
|
||||||
|
'company_name': 'ABC Hospital Ltd.'
|
||||||
}
|
}
|
||||||
}
|
}
|
166
erpnext/demo/setup/healthcare.py
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
import frappe, json
|
||||||
|
from frappe.utils.make_random import get_random
|
||||||
|
import datetime
|
||||||
|
from erpnext.demo.setup.setup_data import import_json
|
||||||
|
from frappe.utils import getdate
|
||||||
|
from erpnext.healthcare.doctype.lab_test.lab_test import create_test_from_template
|
||||||
|
|
||||||
|
def setup_data():
|
||||||
|
frappe.flags.mute_emails = True
|
||||||
|
make_masters()
|
||||||
|
make_patient()
|
||||||
|
make_lab_test()
|
||||||
|
make_consulation()
|
||||||
|
make_appointment()
|
||||||
|
consulation_on_appointment()
|
||||||
|
lab_test_on_consultation()
|
||||||
|
frappe.db.commit()
|
||||||
|
frappe.clear_cache()
|
||||||
|
|
||||||
|
def make_masters():
|
||||||
|
import_json("Physician")
|
||||||
|
import_drug()
|
||||||
|
frappe.db.commit()
|
||||||
|
|
||||||
|
def make_patient():
|
||||||
|
file_path = get_json_path("Patient")
|
||||||
|
with open(file_path, "r") as open_file:
|
||||||
|
patient_data = json.loads(open_file.read())
|
||||||
|
count = 1
|
||||||
|
|
||||||
|
for d in enumerate(patient_data):
|
||||||
|
patient = frappe.new_doc("Patient")
|
||||||
|
patient.patient_name = d[1]['patient_name'].title()
|
||||||
|
patient.sex = d[1]['gender']
|
||||||
|
patient.blood_group = "A Positive"
|
||||||
|
patient.date_of_birth = datetime.datetime(1990, 3, 25)
|
||||||
|
patient.email_id = d[1]['patient_name'] + "_" + patient.date_of_birth.strftime('%m/%d/%Y') + "@example.com"
|
||||||
|
if count <5:
|
||||||
|
patient.insert()
|
||||||
|
frappe.db.commit()
|
||||||
|
count+=1
|
||||||
|
|
||||||
|
def make_appointment():
|
||||||
|
i = 1
|
||||||
|
while i <= 4:
|
||||||
|
physician = get_random("Physician")
|
||||||
|
department = frappe.get_value("Physician", physician, "department")
|
||||||
|
patient = get_random("Patient")
|
||||||
|
patient_sex = frappe.get_value("Patient", patient, "sex")
|
||||||
|
appointment = frappe.new_doc("Patient Appointment")
|
||||||
|
startDate = datetime.datetime.now()
|
||||||
|
for x in random_date(startDate,0):
|
||||||
|
appointment_datetime = x
|
||||||
|
appointment.appointment_datetime = appointment_datetime
|
||||||
|
appointment.appointment_time = appointment_datetime
|
||||||
|
appointment.appointment_date = appointment_datetime
|
||||||
|
appointment.patient = patient
|
||||||
|
appointment.patient_sex = patient_sex
|
||||||
|
appointment.physician = physician
|
||||||
|
appointment.department = department
|
||||||
|
appointment.save(ignore_permissions = True)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
def make_consulation():
|
||||||
|
for i in xrange(3):
|
||||||
|
physician = get_random("Physician")
|
||||||
|
department = frappe.get_value("Physician", physician, "department")
|
||||||
|
patient = get_random("Patient")
|
||||||
|
patient_sex = frappe.get_value("Patient", patient, "sex")
|
||||||
|
consultation = set_consultation(patient, patient_sex, physician, department, getdate(), i)
|
||||||
|
consultation.save(ignore_permissions=True)
|
||||||
|
|
||||||
|
def consulation_on_appointment():
|
||||||
|
for i in xrange(3):
|
||||||
|
appointment = get_random("Patient Appointment")
|
||||||
|
appointment = frappe.get_doc("Patient Appointment",appointment)
|
||||||
|
consultation = set_consultation(appointment.patient, appointment.patient_sex, appointment.physician, appointment.department, appointment.appointment_date, i)
|
||||||
|
consultation.appointment = appointment.name
|
||||||
|
consultation.save(ignore_permissions=True)
|
||||||
|
|
||||||
|
def set_consultation(patient, patient_sex, physician, department, consultation_date, i):
|
||||||
|
consultation = frappe.new_doc("Consultation")
|
||||||
|
consultation.patient = patient
|
||||||
|
consultation.patient_sex = patient_sex
|
||||||
|
consultation.physician = physician
|
||||||
|
consultation.visit_department = department
|
||||||
|
consultation.consultation_date = consultation_date
|
||||||
|
if i > 2 and patient_sex=='Female':
|
||||||
|
consultation.symptoms = "Having chest pains for the last week."
|
||||||
|
consultation.diagnosis = """This patient's description of dull, aching,
|
||||||
|
exertion related substernal chest pain is suggestive of ischemic
|
||||||
|
cardiac origin. Her findings of a FH of early ASCVD, hypertension,
|
||||||
|
and early surgical menopause are pertinent risk factors for development
|
||||||
|
of coronary artery disease. """
|
||||||
|
else:
|
||||||
|
consultation = append_drug_rx(consultation)
|
||||||
|
consultation = append_test_rx(consultation)
|
||||||
|
return consultation
|
||||||
|
|
||||||
|
def make_lab_test():
|
||||||
|
physician = get_random("Physician")
|
||||||
|
patient = get_random("Patient")
|
||||||
|
patient_sex = frappe.get_value("Patient", patient, "sex")
|
||||||
|
template = get_random("Lab Test Template")
|
||||||
|
set_lab_test(patient, patient_sex, physician, template)
|
||||||
|
|
||||||
|
def lab_test_on_consultation():
|
||||||
|
i = 1
|
||||||
|
while i <= 2:
|
||||||
|
test_rx = get_random("Lab Prescription", filters={'test_created': 0})
|
||||||
|
test_rx = frappe.get_doc("Lab Prescription", test_rx)
|
||||||
|
consultation = frappe.get_doc("Consultation", test_rx.parent)
|
||||||
|
set_lab_test(consultation.patient, consultation.patient_sex, consultation.physician, test_rx.test_code, test_rx.name)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
def set_lab_test(patient, patient_sex, physician, template, rx=None):
|
||||||
|
lab_test = frappe.new_doc("Lab Test")
|
||||||
|
lab_test.physician = physician
|
||||||
|
lab_test.patient = patient
|
||||||
|
lab_test.patient_sex = patient_sex
|
||||||
|
lab_test.template = template
|
||||||
|
lab_test.prescription = rx
|
||||||
|
create_test_from_template(lab_test)
|
||||||
|
|
||||||
|
def append_test_rx(consultation):
|
||||||
|
i = 1
|
||||||
|
while i <= 2:
|
||||||
|
test_rx = consultation.append("test_prescription")
|
||||||
|
test_rx.test_code = get_random("Lab Test Template")
|
||||||
|
i += 1
|
||||||
|
return consultation
|
||||||
|
|
||||||
|
def append_drug_rx(consultation):
|
||||||
|
i = 1
|
||||||
|
while i <= 3:
|
||||||
|
drug = get_random("Item", filters={"item_group":"Drug"})
|
||||||
|
drug = frappe.get_doc("Item", drug)
|
||||||
|
drug_rx = consultation.append("drug_prescription")
|
||||||
|
drug_rx.drug_code = drug.item_code
|
||||||
|
drug_rx.drug_name = drug.item_name
|
||||||
|
drug_rx.dosage = get_random("Prescription Dosage")
|
||||||
|
drug_rx.period = get_random("Prescription Duration")
|
||||||
|
i += 1
|
||||||
|
return consultation
|
||||||
|
|
||||||
|
def random_date(start,l):
|
||||||
|
current = start
|
||||||
|
while l >= 0:
|
||||||
|
curr = current + datetime.timedelta(minutes=60)
|
||||||
|
yield curr
|
||||||
|
l-=1
|
||||||
|
|
||||||
|
def import_drug():
|
||||||
|
frappe.flags.in_import = True
|
||||||
|
data = json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data', 'drug_list.json')).read())
|
||||||
|
for d in data:
|
||||||
|
doc = frappe.new_doc("Item")
|
||||||
|
doc.update(d)
|
||||||
|
doc.insert()
|
||||||
|
frappe.flags.in_import = False
|
||||||
|
|
||||||
|
def get_json_path(doctype):
|
||||||
|
return frappe.get_app_path('erpnext', 'demo', 'data', frappe.scrub(doctype) + '.json')
|
@ -184,7 +184,8 @@ def setup_user_roles():
|
|||||||
user.add_roles('HR User', 'HR Manager', 'Accounts User', 'Accounts Manager',
|
user.add_roles('HR User', 'HR Manager', 'Accounts User', 'Accounts Manager',
|
||||||
'Stock User', 'Stock Manager', 'Sales User', 'Sales Manager', 'Purchase User',
|
'Stock User', 'Stock Manager', 'Sales User', 'Sales Manager', 'Purchase User',
|
||||||
'Purchase Manager', 'Projects User', 'Manufacturing User', 'Manufacturing Manager',
|
'Purchase Manager', 'Projects User', 'Manufacturing User', 'Manufacturing Manager',
|
||||||
'Support Team', 'Academics User')
|
'Support Team', 'Academics User', 'Physician', 'Healthcare Administrator', 'Laboratory User',
|
||||||
|
'Nursing User', 'Patient')
|
||||||
|
|
||||||
if not frappe.db.get_global('demo_hr_user'):
|
if not frappe.db.get_global('demo_hr_user'):
|
||||||
user = frappe.get_doc('User', 'CharmaineGaudreau@example.com')
|
user = frappe.get_doc('User', 'CharmaineGaudreau@example.com')
|
||||||
@ -387,5 +388,3 @@ def import_json(doctype, submit=False, values=None):
|
|||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
|
|
||||||
frappe.flags.in_import = False
|
frappe.flags.in_import = False
|
||||||
|
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 818 KiB After Width: | Height: | Size: 818 KiB |
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
BIN
erpnext/docs/assets/img/healthcare/._.DS_Store
Executable file
BIN
erpnext/docs/assets/img/healthcare/._appointment_1.png
Executable file
BIN
erpnext/docs/assets/img/healthcare/._appointment_2.png
Executable file
BIN
erpnext/docs/assets/img/healthcare/._appointment_3.png
Executable file
BIN
erpnext/docs/assets/img/healthcare/._consultation_1.png
Executable file
BIN
erpnext/docs/assets/img/healthcare/._consultation_2.png
Executable file
BIN
erpnext/docs/assets/img/healthcare/._consultation_3.png
Executable file
BIN
erpnext/docs/assets/img/healthcare/._consultation_4.png
Executable file
BIN
erpnext/docs/assets/img/healthcare/._home.png
Executable file
BIN
erpnext/docs/assets/img/healthcare/._lab_test_1.png
Executable file
BIN
erpnext/docs/assets/img/healthcare/._lab_test_2.png
Executable file
BIN
erpnext/docs/assets/img/healthcare/._medical_code_1.png
Executable file
BIN
erpnext/docs/assets/img/healthcare/._medical_record_1.png
Executable file
BIN
erpnext/docs/assets/img/healthcare/._medical_record_2.png
Executable file
BIN
erpnext/docs/assets/img/healthcare/._module.png
Executable file
BIN
erpnext/docs/assets/img/healthcare/._patient_1.png
Executable file
BIN
erpnext/docs/assets/img/healthcare/._patient_2.png
Executable file
BIN
erpnext/docs/assets/img/healthcare/._patient_3.png
Executable file
BIN
erpnext/docs/assets/img/healthcare/._physician_1.png
Executable file
BIN
erpnext/docs/assets/img/healthcare/._physician_2.png
Executable file
BIN
erpnext/docs/assets/img/healthcare/._physician_schedule_1.png
Executable file
BIN
erpnext/docs/assets/img/healthcare/._physician_schedule_2.png
Executable file
BIN
erpnext/docs/assets/img/healthcare/._sample_collection_1.png
Executable file
BIN
erpnext/docs/assets/img/healthcare/._vitals_1.png
Executable file
BIN
erpnext/docs/assets/img/healthcare/._vitals_2.png
Executable file
0
erpnext/subscription/__init__.py → erpnext/docs/assets/img/healthcare/__init__.py
Normal file → Executable file
BIN
erpnext/docs/assets/img/healthcare/appointment_1.png
Executable file
After Width: | Height: | Size: 28 KiB |
BIN
erpnext/docs/assets/img/healthcare/appointment_2.png
Executable file
After Width: | Height: | Size: 64 KiB |
BIN
erpnext/docs/assets/img/healthcare/appointment_3.png
Executable file
After Width: | Height: | Size: 26 KiB |
BIN
erpnext/docs/assets/img/healthcare/consultation_1.png
Executable file
After Width: | Height: | Size: 98 KiB |
BIN
erpnext/docs/assets/img/healthcare/consultation_2.png
Executable file
After Width: | Height: | Size: 24 KiB |
BIN
erpnext/docs/assets/img/healthcare/consultation_3.png
Executable file
After Width: | Height: | Size: 40 KiB |
BIN
erpnext/docs/assets/img/healthcare/consultation_4.png
Executable file
After Width: | Height: | Size: 22 KiB |
BIN
erpnext/docs/assets/img/healthcare/home.png
Executable file
After Width: | Height: | Size: 73 KiB |
BIN
erpnext/docs/assets/img/healthcare/lab_test_1.png
Executable file
After Width: | Height: | Size: 97 KiB |
BIN
erpnext/docs/assets/img/healthcare/lab_test_2.png
Executable file
After Width: | Height: | Size: 56 KiB |
BIN
erpnext/docs/assets/img/healthcare/medical_code_1.png
Executable file
After Width: | Height: | Size: 127 KiB |
BIN
erpnext/docs/assets/img/healthcare/medical_record_1.png
Executable file
After Width: | Height: | Size: 114 KiB |
BIN
erpnext/docs/assets/img/healthcare/medical_record_2.png
Executable file
After Width: | Height: | Size: 58 KiB |
BIN
erpnext/docs/assets/img/healthcare/module.png
Executable file
After Width: | Height: | Size: 110 KiB |
BIN
erpnext/docs/assets/img/healthcare/patient_1.png
Executable file
After Width: | Height: | Size: 104 KiB |
BIN
erpnext/docs/assets/img/healthcare/patient_2.png
Executable file
After Width: | Height: | Size: 73 KiB |