Fixed merge conflict

This commit is contained in:
Nabin Hait 2017-09-21 12:53:41 +05:30
commit 24ec3c7dcb
526 changed files with 57529 additions and 13085 deletions

View File

@ -51,11 +51,17 @@ before_script:
- bench start & - bench start &
- sleep 10 - sleep 10
script: jobs:
include:
- stage: test
script:
- set -e - set -e
- bench run-tests - bench run-tests
- sleep 5 env: Server Side Test
- bench reinstall --yes - # stage
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
env: Client Side Test

View File

@ -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)

View File

@ -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);

View File

@ -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()
@ -498,10 +502,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"))
@ -514,10 +520,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
@ -632,7 +642,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
@ -675,19 +689,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:
@ -703,6 +721,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))

View File

@ -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()
]); ]);

View File

@ -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'),

View File

@ -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",

View File

@ -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

View File

@ -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,
@ -3858,7 +3920,7 @@
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"menu_index": 0, "menu_index": 0,
"modified": "2017-08-31 11:22:47.074420", "modified": "2017-09-19 11:22:47.074420",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Invoice", "name": "Purchase Invoice",

View File

@ -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
@ -584,6 +586,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()

View File

@ -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,
@ -4749,7 +4811,7 @@
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"menu_index": 0, "menu_index": 0,
"modified": "2017-08-31 11:23:08.675028", "modified": "2017-09-19 11:23:08.675028",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Invoice", "name": "Sales Invoice",

View File

@ -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
@ -784,6 +786,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"):

View File

@ -215,12 +215,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",
@ -285,7 +285,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"):
@ -294,10 +294,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",
@ -327,7 +329,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:
@ -423,13 +426,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"):
@ -437,28 +439,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
@ -486,7 +488,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
}, },
@ -500,7 +502,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
} }
@ -536,8 +538,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()
@ -1286,6 +1291,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)

View File

@ -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:

View File

@ -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):

View File

@ -136,14 +136,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",
@ -165,6 +158,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"):

View File

@ -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

View File

@ -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,20 +296,26 @@ 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}
order by order by
`tabSales Invoice`.posting_date desc, `tabSales Invoice`.posting_time desc""" `tabSales Invoice`.posting_date desc, `tabSales Invoice`.posting_time desc"""
.format(conditions=conditions, sales_person_cols=sales_person_cols, .format(conditions=conditions, sales_person_cols=sales_person_cols,

View File

@ -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,
@ -3396,7 +3458,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-08-31 11:22:30.190589", "modified": "2017-09-19 11:22:30.190589",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Purchase Order", "name": "Purchase Order",

View File

@ -65,7 +65,7 @@ QUnit.test("test: request_for_quotation", function(assert) {
assert.ok(cur_frm.doc.docstatus == 1, "Quotation request submitted"); assert.ok(cur_frm.doc.docstatus == 1, "Quotation request submitted");
}, },
() => frappe.click_button('Send Supplier Emails'), () => frappe.click_button('Send Supplier Emails'),
() => frappe.timeout(3), () => frappe.timeout(4),
() => { () => {
assert.ok($('div.modal.fade.in > div.modal-dialog > div > div.modal-body.ui-front > div.msgprint').text().includes("Email sent to supplier Test Supplier"), "Send emails working"); assert.ok($('div.modal.fade.in > div.modal-dialog > div > div.modal-body.ui-front > div.msgprint').text().includes("Email sent to supplier Test Supplier"), "Send emails working");
}, },

View File

@ -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'),

View File

@ -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):

View File

@ -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");
}, },

View File

@ -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,
@ -2308,7 +2370,7 @@
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"menu_index": 0, "menu_index": 0,
"modified": "2017-08-31 11:23:25.268924", "modified": "2017-09-19 11:23:25.268924",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Supplier Quotation", "name": "Supplier Quotation",

View File

@ -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},
] ]
]} ]}

View File

@ -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")
} }
] ]

View 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.")
}
]
}
]

View File

@ -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"

View File

@ -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])

View File

@ -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(

View File

@ -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({

File diff suppressed because it is too large Load Diff

View 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",

View 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"
}
]

View 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"
}
]

View File

@ -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()

View File

@ -15,5 +15,8 @@ data = {
}, },
'Education': { 'Education': {
'company_name': 'Whitmore College' 'company_name': 'Whitmore College'
},
'Healthcare': {
'company_name': 'ABC Hospital Ltd.'
} }
} }

View 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')

View File

@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -11,7 +11,7 @@ You can insert Custom Link Field by following steps below.
####Step 2: Select Form ####Step 2: Select Form
In Customize Form, select Document Type (Quotation, Sales Order, Purchase Invoice Item etc.). Once field are updated in table, open field before which you wish to insert Custom Field. Then click on "Insert Above" to insert new Custom Field. In Customize Form, select Document Type (Quotation, Sales Order, Purchase Invoice Item etc.). Once fields are updated in the accompanying table below, open a field above the one you wish to insert your Custom Field. Then click on "Insert Above" to insert the new Custom Field.
<img alt="Select Docytpe" class="screenshot" src="/docs/assets/img/articles/link-field-1.gif"> <img alt="Select Docytpe" class="screenshot" src="/docs/assets/img/articles/link-field-1.gif">

Binary file not shown.

View File

@ -0,0 +1,38 @@
# Patient Appointment
ERPNext Healthcare allows you to book Patient appointments for any date and if configured, send them alerts via Email or SMS.
You can create a Patient Appointment from
> Healthcare > Patient Appointment > New Patient Appointment
You can book appointments for a registered Patient by searching and selecting the Patient field. You can search the Patient by Patient ID, Name, Email or Mobile number. You can also register a new Patient from the Appointment screen by selecting "Create a new patient" in the Patient field.
<img class="screenshot" alt="ERPNext Healthcare" src="/docs/assets/img/healthcare/appointment_1.png">
If you have a front desk executive to manage your appointments, you can configure a user role to have access to Patient Appointment so that she can do the bookings by selecting the Physician whom the Patient wish to consult and the date for booking. "Check Availability" button will pop up all the available time slots with status indicators for the date. She can select a time slot and "Book" the Appointment for the Patient.
<img class="screenshot" alt="ERPNext Healthcare" src="/docs/assets/img/healthcare/appointment_2.png">
After Booking, the scheduled time of the Appointment and duration will be updated and seved in the document.
<img class="screenshot" alt="ERPNext Healthcare" src="/docs/assets/img/healthcare/appointment_3.png">
You can configure ERPNext to send an SMS alert to the Patient about the booking confirmation or a reminder on the day of Appointment by doing necessary configurations in -
> Healthcare > Healthcare Settings > Out Patient SMS Alerts
The screen also allows the executive to select a Referring Physician so that you can track the source the appointment.
### Actions
* Billing: If you collect the consultation fee while booking the Appointment itself you can do so by using the "Create > Invoice" button. This will take you to the ERPNext Accounts Sales Invoice screen.
* Vital Signs: "Create > Vital Signs" button will take you to the new Vital Signs screen to record the vitals of the Patient.
* Consultation: From the Appointment screen you can directly create a Consultation to record the details of patient encounter.
* View Patient Medical Record.
> Note: User should have privileges (User Role) to view the buttons
A Patient can also book an appointment with a Physician by checking the Physician's availability directly through the **ERPNext Portal**.
{next}

View File

@ -0,0 +1,28 @@
# Consultation
ERPNext Healthcare allows you to record Patient encounters through the Consultation document. You can create a Consultation based on a previously booked Appointment or directly by creating a new Consultation
>Healthcare > Consultation > Consultation
If you are creating the Consultation document from an Appointment, Patient and other related data will automatically be populated else you can search the Patient by name, email phone number etc. The Patient Details section will list the latest Vital Signs record of the patient and other information captured in the Patient screen.
<img class="screenshot" alt="ERPNext Healthcare" src="/docs/assets/img/healthcare/consultation_1.png">
### Assessment
Encounter Impression section allows you to select (or create new) Complaints and your assessment based on the presented complaints. You can opt to include the captured data in Consultation print by selecting the "In Print" flag
<img class="screenshot" alt="ERPNext Healthcare" src="/docs/assets/img/healthcare/consultation_2.png">
### Prescriptions
You can prescribe medicines in the Drug Prescription section by selecting the drug codes (Stock Item) and appropriate dosages. If you are not managing Stock and Items are not configured, you can simply enter the Medicine name and strength in the Strength field which will printed.
Prescribing a laboratory investigation is similar and if you have Lab Tests configured, you can select from the list. Or key in the Lab Test name to be printed as part of the Prescription.
<img class="screenshot" alt="ERPNext Healthcare" src="/docs/assets/img/healthcare/consultation_3.png">
### Medical Coding
You can also attach one or more Medical Codes to designate the Diagnosis in the Medical Coding Section. You will have to select the Medical Code Standard you wish to encode the diagnosis and then select the Code by searching the Code itself or the Code Description.
<img class="screenshot" alt="ERPNext Healthcare" src="/docs/assets/img/healthcare/consultation_4.png">
{next}

View File

@ -0,0 +1,13 @@
# Healthcare
ERPNext Healthcare helps you manage your Clinic or Practice efficiently by scheduling **Appointments** and recording **Patient Encounters** (Consultations). You can easily pull out a **Patient's Health Record** anytime to review all the history of treatments assisting you in providing effective, high quality care.
<img class="screenshot" alt="ERPNext Healthcare" src="/docs/assets/img/healthcare/module.png">
Patients can view various documents relevant to them and book Appointments via the **ERPNext Portal**. The healthcare module is integrated with **Accounts** and **Human Resources** modules, helping you in **Billing**, **Payroll Management** etc. and benefit from other rich features of ERPNext. You can configure the **Selling** and **Stock** modules manage your Pharmacy.
ERPNext Healthcare also includes features for effectively managing the functions of an associated **Laboratory** by helping you record **Sample Collection**, emailing and printing **Lab Test** results etc. ERPNext Healthcare allows you to upload **Medical Code Standards** like **ICD10** and attach to Consultations.
### Topics
{index}

View File

@ -0,0 +1,12 @@
patient
appointment
vital_signs
consultation
medical_record
sample_collection
lab_test
invoicing
physician
physician_schedule
medical_codes
setup

View File

@ -0,0 +1,8 @@
# Invoicing
Billing is an integral part of any undertaking and ERPNext Healthcare achieves this by making use of the ERPNext Accounts module.
> Note: All transactions of a Patient is booked against the Customer which it is linked to.
All ERPNext Healthcare documents which require Invoicing will have buttons which would take you to the Sales Invoice with the Items configured for the service. You can then proceed by following the ERPNExt Accounts module workflows. Please note that your User account should have appropriate privileges to access the Accounts documents.
{next}

View File

@ -0,0 +1,22 @@
# Lab Test
ERPNext Healthcare allows you to manage a clinical laboratory efficiently by allowing you to enter Lab Tests and print or email test results, manage samples collected, create Invoice etc. ERPNext Healthcare comes pre-packed with some sample tests, you can reconfigure Lab Test Templates for each Test and its result format or crate new ones. You can do this in
>Healthcare > Setup > Lab Text Templates
Once you have all necessary Lab Test Templates configured, you can start creating Lab Tests by selecting a Test Template every time you create a Test. To create a new Lab Test
>Healthcare > Laboratory > Lab Test > New Lab Test
<img class="screenshot" alt="ERPNext Healthcare" src="/docs/assets/img/healthcare/lab_test_1.png">
You can record the test results in the Lab Test document as the results gets ready.
<img class="screenshot" alt="ERPNext Healthcare" src="/docs/assets/img/healthcare/lab_test_2.png">
> Note: To create Sample Collection documents for every Lab Test, check "Manage Sample Collection" flag in Healthcare Settings and select Sample in the Lab Test Template
In many Laboratories, approval of Lab Tests is a must before printing and submitting the document. ERPNext Healthcare allows you to create Users with Role "Lab Test Approver" for this. You will also have to enable this in
>Healthcare Settings > Laboratory Settings > Require Lab Test Approval
This will ensure that emailing or printing of Lab Tests can only be done after Approval of the Lab Test by the Lab Test Approver.
{next}

View File

@ -0,0 +1,9 @@
# Medical Code Standards
Medical Coding are in many countries required for regulatory compliance and many of the Medical Insurance companies do that pricing based on Medical Code standards. ERPNext Healthcare offers support, however limited, to encode diagnosis and assessments recorded as part of Consultation. This can be done if you configure the Medical Code Standard and related Medical Codes - this is easily done by data import as the code data tends to be quite large. You can create as many Medical Code Standards you wish
> Healthcare > Masters > Medical Code Standard
Medical Code Standard document is used to name the Code Standard and act as a container for all the medical codes which are standardized under it. Medical Codes and descriptions can then be imported to the Medical Code document, after ensuring that you set the Medical Code Standard field to the appropriate Standard name.
<img class="screenshot" alt="ERPNext Healthcare" src="/docs/assets/img/healthcare/medical_code_1.png">
{next}

View File

@ -0,0 +1,13 @@
# Patient Medical Record
The maintenance of complete and accurate medical records is a requirement of healthcare providers and is critical in rendering effective, high quality care. ERPNext Healthcare allows you to draw up the treatment history of a Patient anytime by merely selecting the Patient. "Medical Record" button is available in various screens so that you can easily switch to the Medical Record page to view the patient history.
Medical Record automatically keeps track of all Consultations, recorded Vital Signs, Lab Investigations etc. Complaints, Diagnosis etc. captured as part of consultation are easily viewable but to look at the details of other documents, links are provided.
<img class="screenshot" alt="ERPNext Healthcare" src="/docs/assets/img/healthcare/medical_record_1.png">
##### Adding notes manually to Medical Record
In the Patient screen Create > Medical Record will allow you to record notes to the Medical Record manually. You can also attach files when doing this, and the Medical Record will display links to the attached file along side the notes. Create > Medical Record button is also made available in the Consultation screen
<img class="screenshot" alt="ERPNext Healthcare" src="/docs/assets/img/healthcare/medical_record_2.png">
{next}

Some files were not shown because too many files have changed in this diff Show More