Compare commits

..

46 Commits

Author SHA1 Message Date
Saurabh
7b833802a9 Merge branch 'hotfix' 2017-11-28 11:00:17 +05:30
Saurabh
d86ace41a1 bumped to version 9.2.17 2017-11-28 11:30:17 +06:00
Jamsheer
b488475d92 Remove Company from Patient - Patches Added (#11716)
* Remove Company from Patient - Patches Added

* Update patient DOB field label to Date of birth

* Patient marital status default null

* Update patient.py
2017-11-28 10:47:06 +05:30
Saurabh
8eda0cc8ba [fix] validate bom if order type is subcontracting (#11705)
* [fix] validate bom if order type is subcontracting

* [fix] moved validation to server side
2017-11-28 10:41:35 +05:30
Saurabh
9d0092f89a [fix] pull source warehouse from production order child table (#11718) 2017-11-27 12:04:41 +05:30
Shreya Shah
e3d6d21ec5 [Fix] Item name and description in production order (#11723)
* fetch item name and description from bom

* Update production_order.py

* patch added

* Update set_item_name_in_production_order.py
2017-11-27 12:02:13 +05:30
Saurabh
ab5e77ecf1 [fix] do not pull disabled pos profiles (#11733) 2017-11-27 11:26:13 +05:30
Saurabh
41f60546a1 [fix] do not allow user to create party type (#11706) 2017-11-23 18:02:51 +05:30
Nabin Hait
cda4d50063 Fixed reserved qty for production logic and patch for reposting (#11691) 2017-11-23 13:05:43 +05:30
Zarrar
a90274fed9 linking issue in dashboard (#11701) 2017-11-23 12:19:40 +05:30
Jamsheer
1ce56316ea Populate patient_name in Healthcare Doctypes (#11690)
* On Cancel Consultation - Open Appointment - Delete Medical Record

* Add Filter on Account Selection - is_group: 0

* Patient Medical Record - List view Fixes

* Update consultation.py

* Set Appointment Refrence in Vital Signs

* Add Other in Gender Selection

* Populate patient_name in Healthcare Doctypes
2017-11-23 12:18:43 +05:30
Saurabh
7cf945c975 Merge branch 'hotfix' 2017-11-22 17:54:18 +05:30
Saurabh
4bc12b68e4 bumped to version 9.2.16 2017-11-22 18:24:18 +06:00
rohitwaghchaure
4dc5f0efaf [Fix] Item details not fetching if item has no default bom (#11688) 2017-11-22 15:21:47 +05:30
Nabin Hait
96abfd2ab9 [fix] In payment entry, run some events serially to avoid async issue 2017-11-22 15:13:16 +05:30
Makarand Bauskar
75443a94ee [minor] set the pos profile name while creating demo (#11664) 2017-11-21 16:12:40 +05:30
Nabin Hait
cc884578b5 Update sales_order.py 2017-11-21 12:24:03 +05:30
rohitwaghchaure
179e0c1d8d Added party name in accounts receivable/payable report (#11656) 2017-11-20 11:18:55 +05:30
rohitwaghchaure
34d6340be6 Merge pull request #11647 from rohitwaghchaure/pos_selling_price_issue
[fix] Price list not loaded from pos profile, auto set outstanding amount in the mode of payment
2017-11-18 21:33:32 +05:30
Rohit Waghchaure
77940493a8 [fix] Price list not loaded from pos profile, auto set outstanding amount in the mode of payment 2017-11-18 19:06:31 +05:30
Saurabh
a5b53e9480 Merge branch 'hotfix' 2017-11-17 14:55:23 +05:30
Saurabh
f773af6053 bumped to version 9.2.15 2017-11-17 15:25:23 +06:00
Saurabh
119a50e228 Merge pull request #11630 from saurabh6790/domain_settings_fix
[fix] set default language in local
2017-11-17 14:54:38 +05:30
Saurabh
11ec2c50e0 [fix] set default language in local 2017-11-17 14:51:08 +05:30
Saurabh
1e74519726 Merge branch 'hotfix' 2017-11-17 14:01:30 +05:30
Saurabh
05ed86a00e bumped to version 9.2.14 2017-11-17 14:31:29 +06:00
Saurabh
d40ae81fc9 Merge pull request #11627 from rohitwaghchaure/pos_redirect_issue
[fix] POS redirect issue
2017-11-17 13:55:10 +05:30
Rohit Waghchaure
44f7b157ff [fix] POS redirect issue 2017-11-17 13:52:11 +05:30
Saurabh
3499ba08df [fix] do not translate domains while creating db record (#11616) 2017-11-17 12:29:26 +05:30
rohitwaghchaure
51a397c97f [fix] Valuation rate in stock entry and code cleanup (#11614) 2017-11-16 18:23:16 +05:30
Manas Solanki
bb34c57603 link the address and contact with the sales partner (#11592) 2017-11-16 18:10:04 +05:30
tundebabzy
f7e6934d7c handle None case in update_reserved_qty_for_production (#11593) 2017-11-16 14:13:49 +05:30
rohitwaghchaure
21cbbae88f [fix] Payment reconcillation showing linked journal entries (#11611) 2017-11-16 14:05:57 +05:30
Nabin Hait
94704beba3 Merge branch 'hotfix' 2017-11-15 14:08:17 +05:30
Nabin Hait
429e5d57d5 bumped to version 9.2.13 2017-11-15 14:38:17 +06:00
Jamsheer
0abec034df Healthcare hotfix (#11586)
* On Cancel Consultation - Open Appointment - Delete Medical Record

* Add Filter on Account Selection - is_group: 0

* Patient Medical Record - List view Fixes

* Update consultation.py
2017-11-15 13:53:50 +05:30
Umair Sayed
3a9ca883b9 Update party.py (#11584) 2017-11-15 13:44:04 +05:30
Manas Solanki
bf59b5927f make route from item code if item name is None (#11579) 2017-11-15 10:55:22 +05:30
Jamsheer
bab226698f Healthcare hotfix (#11574)
* Patient dashboard - Vital Signs

* Remove submit validation in Consultation

* Physician Schedule - Add Time Slots - Fix

* Patient Appointment - Client side - fixes
2017-11-15 09:47:00 +05:30
rohitwaghchaure
43edd5d03c Employee loan fixes (#11495)
* Employee loan fixes

* Update update_employee_loan_details.py
2017-11-14 17:59:20 +05:30
Nabin Hait
3f83afe4e1 Merge branch 'hotfix' 2017-11-14 17:48:42 +05:30
Nabin Hait
ea3e6b93a7 bumped to version 9.2.12 2017-11-14 18:18:42 +06:00
Nabin Hait
42274a4591 Delete healthcare domain items (#11567) 2017-11-14 17:15:11 +05:30
Nabin Hait
66f460ffbc Merge branch 'hotfix' 2017-11-14 13:58:37 +05:30
Nabin Hait
0590d1da05 bumped to version 9.2.11 2017-11-14 14:28:37 +06:00
Rushabh Mehta
98aa581864 [fix] default tax only on insert (#11544)
* [fix] default tax only on insert

* [fix] default tax only on insert

* [fix] default tax only on insert
2017-11-14 09:32:32 +05:30
61 changed files with 1227 additions and 554 deletions

View File

@@ -4,7 +4,7 @@ import inspect
import frappe
from erpnext.hooks import regional_overrides
__version__ = '9.2.10'
__version__ = '9.2.17'
def get_default_company(user=None):
'''Get default company for user'''

View File

@@ -253,20 +253,24 @@ frappe.ui.form.on('Payment Entry', {
},
callback: function(r, rt) {
if(r.message) {
if(frm.doc.payment_type == "Receive") {
frm.set_value("paid_from", r.message.party_account);
frm.set_value("paid_from_account_currency", r.message.party_account_currency);
frm.set_value("paid_from_account_balance", r.message.account_balance);
} else if (frm.doc.payment_type == "Pay"){
frm.set_value("paid_to", r.message.party_account);
frm.set_value("paid_to_account_currency", r.message.party_account_currency);
frm.set_value("paid_to_account_balance", r.message.account_balance);
}
frm.set_value("party_balance", r.message.party_balance);
frm.events.get_outstanding_documents(frm);
frm.events.hide_unhide_fields(frm);
frm.events.set_dynamic_labels(frm);
frm.set_party_account_based_on_party = false;
frappe.run_serially([
() => {
if(frm.doc.payment_type == "Receive") {
frm.set_value("paid_from", r.message.party_account);
frm.set_value("paid_from_account_currency", r.message.party_account_currency);
frm.set_value("paid_from_account_balance", r.message.account_balance);
} else if (frm.doc.payment_type == "Pay"){
frm.set_value("paid_to", r.message.party_account);
frm.set_value("paid_to_account_currency", r.message.party_account_currency);
frm.set_value("paid_to_account_balance", r.message.account_balance);
}
},
() => frm.set_value("party_balance", r.message.party_balance),
() => frm.events.get_outstanding_documents(frm),
() => frm.events.hide_unhide_fields(frm),
() => frm.events.set_dynamic_labels(frm),
() => { frm.set_party_account_based_on_party = false; }
]);
}
}
});

View File

@@ -24,11 +24,11 @@ frappe.ui.form.on("POS Profile", "onload", function(frm) {
frappe.ui.form.on('POS Profile', {
setup: function(frm) {
frm.set_query("online_print_format", function() {
frm.set_query("print_format_for_online", function() {
return {
filters: [
['Print Format', 'doc_type', '=', 'Sales Invoice'],
['Print Format', 'print_format_type', '!=', 'Js'],
['Print Format', 'print_format_type', '=', 'Server'],
]
};
});

View File

@@ -19,7 +19,7 @@ class POSProfile(Document):
def check_for_duplicate(self):
res = frappe.db.sql("""select name, user from `tabPOS Profile`
where ifnull(user, '') = %s and name != %s and company = %s""",
where ifnull(user, '') = %s and name != %s and company = %s and ifnull(disabled, 0) != 1""",
(self.user, self.name, self.company))
if res:
if res[0][1]:

View File

@@ -12,8 +12,5 @@ class POSSettings(Document):
def set_link_for_pos(self):
link = 'pos' if self.use_pos_in_offline_mode else 'point-of-sale'
desktop_icon = frappe.db.get_value('Desktop Icon',
{'standard': 1, 'module_name': 'POS'}, 'name')
if desktop_icon:
frappe.db.set_value('Desktop Icon', desktop_icon, 'link', link)
frappe.db.sql(""" update `tabDesktop Icon` set link = '{0}'
where module_name like '%pos%'""".format(link))

View File

@@ -247,7 +247,7 @@ class SalesInvoice(SellingController):
super(SalesInvoice, self).set_missing_values(for_validate)
if pos:
return {"print_format": pos.get("print_format") }
return {"print_format": pos.get("print_format_for_online") }
def update_time_sheet(self, sales_invoice):
for d in self.timesheets:

View File

@@ -200,9 +200,6 @@ def get_party_account(party_type, party, company):
if (account and account_currency != existing_gle_currency) or not account:
account = get_party_gle_account(party_type, party, company)
if not account:
frappe.throw(_("Party account not specified, please setup default party account in company"))
return account
def get_party_account_currency(party_type, party, company):

View File

@@ -36,8 +36,14 @@
<br>{%= data[i][__("Voucher No")] %}</td>
<td>
{% if(!(filters.customer || filters.supplier)) { %}
{%= data[i][__("Customer Name")] || data[i][__("Customer")] || data[i][__("Supplier Name")] || data[i][__("Supplier")] %}<br>{%= __("Remarks") %}:
{%= data[i][__("Customer")] || data[i][__("Supplier")] %}
{% if(data[i][__("Customer Name")] && data[i][__("Customer Name")] != data[i][__("Customer")]) { %}
<br> {%= data[i][__("Customer Name")] %}
{% } else if(data[i][__("Supplier Name")] != data[i][__("Supplier")]) { %}
<br> {%= data[i][__("Supplier Name")] %}
{% } %}
{% } %}
<br>{%= __("Remarks") %}:
{%= data[i][__("Remarks")] %}
</td>
<td style="text-align: right">
@@ -66,8 +72,13 @@
<td>
{% if(!(filters.customer || filters.supplier)) { %}
{%= data[i][__("Customer")] || data[i][__("Supplier")] %}
<br>{%= __("Remarks") %}:
{% if(data[i][__("Customer Name")] && data[i][__("Customer Name")] != data[i][__("Customer")]) { %}
<br> {%= data[i][__("Customer Name")] %}
{% } else if(data[i][__("Supplier Name")] != data[i][__("Supplier")]) { %}
<br> {%= data[i][__("Supplier Name")] %}
{% } %}
{% } %}
<br>{%= __("Remarks") %}:
{%= data[i][__("Remarks")] %}
</td>
{% } else { %}

View File

@@ -593,7 +593,9 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
select ifnull(sum({payment_dr_or_cr}), 0)
from `tabGL Entry` payment_gl_entry
where payment_gl_entry.against_voucher_type = invoice_gl_entry.voucher_type
and payment_gl_entry.against_voucher = invoice_gl_entry.against_voucher
and if(invoice_gl_entry.voucher_type='Journal Entry',
payment_gl_entry.against_voucher = invoice_gl_entry.voucher_no,
payment_gl_entry.against_voucher = invoice_gl_entry.against_voucher)
and payment_gl_entry.party_type = invoice_gl_entry.party_type
and payment_gl_entry.party = invoice_gl_entry.party
and payment_gl_entry.account = invoice_gl_entry.account

View File

@@ -51,6 +51,7 @@ class PurchaseOrder(BuyingController):
self.validate_with_previous_doc()
self.validate_for_subcontracting()
self.validate_minimum_order_qty()
self.validate_bom_for_subcontracting_items()
self.create_raw_materials_supplied("supplied_items")
self.set_received_qty_for_drop_ship_items()
@@ -95,6 +96,13 @@ class PurchaseOrder(BuyingController):
frappe.throw(_("Item {0}: Ordered qty {1} cannot be less than minimum order qty {2} (defined in Item).").format(item_code,
qty, itemwise_min_order_qty.get(item_code)))
def validate_bom_for_subcontracting_items(self):
if self.is_subcontracted == "Yes":
for item in self.items:
if not item.bom:
frappe.throw(_("BOM is not specified for subcontracting item {0} at row {1}"\
.format(item.item_code, item.idx)))
def get_schedule_dates(self):
for d in self.get('items'):
if d.material_request_item and not d.schedule_date:

View File

@@ -209,10 +209,10 @@ class AccountsController(TransactionBase):
tax_master_doctype = self.meta.get_field("taxes_and_charges").options
if not self.get("taxes"):
if self.is_new() and not self.get("taxes"):
if not self.get("taxes_and_charges"):
# get the default tax master
self.set("taxes_and_charges", frappe.db.get_value(tax_master_doctype, {"is_default": 1}))
self.taxes_and_charges = frappe.db.get_value(tax_master_doctype, {"is_default": 1})
self.append_taxes_from_master(tax_master_doctype)
@@ -608,7 +608,10 @@ def get_tax_rate(account_head):
@frappe.whitelist()
def get_default_taxes_and_charges(master_doctype):
default_tax = frappe.db.get_value(master_doctype, {"is_default": 1})
return get_taxes_and_charges(master_doctype, default_tax)
return {
'taxes_and_charges': default_tax,
'taxes': get_taxes_and_charges(master_doctype, default_tax)
}
@frappe.whitelist()
def get_taxes_and_charges(master_doctype, master_name):

View File

@@ -171,7 +171,7 @@ class BuyingController(StockController):
for item in self.get("items"):
if self.doctype in ["Purchase Receipt", "Purchase Invoice"]:
item.rm_supp_cost = 0.0
if item.item_code in self.sub_contracted_items:
if item.bom and item.item_code in self.sub_contracted_items:
self.update_raw_materials_supplied(item, raw_material_table)
if [item.item_code, item.name] not in parent_items:

View File

@@ -48,16 +48,16 @@ class Opportunity(TransactionBase):
# check if customer is already created agains the self.contact_email
customer = frappe.db.sql("""select
distinct `tabDynamic Link`.link_name as customer
from
from
`tabContact`,
`tabDynamic Link`
where `tabContact`.email_id='{0}'
and
and
`tabContact`.name=`tabDynamic Link`.parent
and
ifnull(`tabDynamic Link`.link_name, '')<>''
and
`tabDynamic Link`.link_doctype='Customer'
ifnull(`tabDynamic Link`.link_name, '')<>''
and
`tabDynamic Link`.link_doctype='Customer'
""".format(self.contact_email), as_dict=True)
if customer and customer[0].customer:
self.customer = customer[0].customer
@@ -118,9 +118,9 @@ class Opportunity(TransactionBase):
def has_ordered_quotation(self):
return frappe.db.sql("""
select q.name
select q.name
from `tabQuotation` q, `tabQuotation Item` qi
where q.name = qi.parent and q.docstatus=1 and qi.prevdoc_docname =%s
where q.name = qi.parent and q.docstatus=1 and qi.prevdoc_docname =%s
and q.status = 'Ordered'""", self.name)
def has_lost_quotation(self):
@@ -233,8 +233,8 @@ def make_quotation(source_name, target_doc=None):
# get default taxes
taxes = get_default_taxes_and_charges("Sales Taxes and Charges Template")
if taxes:
quotation.extend("taxes", taxes)
if taxes.get('taxes'):
quotation.update(taxes)
quotation.run_method("set_missing_values")
quotation.run_method("calculate_taxes_and_totals")

View File

@@ -5,6 +5,7 @@ def get_data():
'fieldname': 'prevdoc_docname',
'non_standard_fieldnames': {
'Supplier Quotation': 'opportunity',
'Quotation': 'opportunity'
},
'transactions': [
{

View File

@@ -348,8 +348,10 @@ def setup_budget():
budget.action_if_annual_budget_exceeded = "Warn"
expense_ledger_count = frappe.db.count("Account", {"is_group": "0", "root_type": "Expense"})
add_random_children(budget, "accounts", rows=random.randint(10, expense_ledger_count), randomize = { "account": ("Account", {"is_group": "0", "root_type": "Expense"})
}, unique="account")
add_random_children(budget, "accounts", rows=random.randint(10, expense_ledger_count),
randomize = {
"account": ("Account", {"is_group": "0", "root_type": "Expense"})
}, unique="account")
for d in budget.accounts:
d.budget_amount = random.randint(5, 100) * 10000
@@ -361,6 +363,7 @@ def setup_pos_profile():
company_abbr = frappe.db.get_value("Company", erpnext.get_default_company(), "abbr")
pos = frappe.new_doc('POS Profile')
pos.user = frappe.db.get_global('demo_accounts_user')
pos.pos_profile_name = "Demo POS Profile"
pos.naming_series = 'SINV-'
pos.update_stock = 0
pos.write_off_account = 'Cost of Goods Sold - '+ company_abbr

View File

@@ -139,6 +139,7 @@ var btn_create_vital_signs = function (frm) {
}
frappe.route_options = {
"patient": frm.doc.patient,
"appointment": frm.doc.appointment
};
frappe.new_doc("Vital Signs");
};

View File

@@ -216,7 +216,7 @@
"label": "Gender",
"length": 0,
"no_copy": 0,
"options": "\nMale\nFemale",
"options": "\nMale\nFemale\nOther",
"permlevel": 0,
"precision": "",
"print_hide": 1,
@@ -1004,7 +1004,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-10-05 12:13:52.596750",
"modified": "2017-11-22 14:03:30.434304",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Consultation",

View File

@@ -8,12 +8,12 @@ from frappe import _
from frappe.model.document import Document
from frappe.utils import getdate
import json
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account,get_income_account
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account, get_income_account
class Consultation(Document):
def on_update(self):
if(self.appointment):
frappe.db.set_value("Patient Appointment",self.appointment,"status","Closed")
frappe.db.set_value("Patient Appointment", self.appointment, "status", "Closed")
update_consultation_to_medical_record(self)
def after_insert(self):
@@ -23,9 +23,10 @@ class Consultation(Document):
if not self.diagnosis or not self.symptoms:
frappe.throw("Diagnosis and Complaints cannot be left blank")
physician = frappe.get_doc("Physician",self.physician)
if(frappe.session.user != physician.user_id):
frappe.throw(_("You don't have permission to submit"))
def on_cancel(self):
if(self.appointment):
frappe.db.set_value("Patient Appointment", self.appointment, "status", "Open")
delete_medical_record(self)
def set_sales_invoice_fields(company, patient):
sales_invoice = frappe.new_doc("Sales Invoice")
@@ -91,8 +92,8 @@ def create_invoice_items(physician, invoice, company):
item_line.qty = 1
item_line.uom = "Nos"
item_line.conversion_factor = 1
item_line.income_account = get_income_account(physician,company)
op_consulting_charge = frappe.get_value("Physician",physician,"op_consulting_charge")
item_line.income_account = get_income_account(physician, company)
op_consulting_charge = frappe.get_value("Physician", physician, "op_consulting_charge")
if op_consulting_charge:
item_line.rate = op_consulting_charge
item_line.amount = op_consulting_charge
@@ -111,10 +112,13 @@ def insert_consultation_to_medical_record(doc):
medical_record.save(ignore_permissions=True)
def update_consultation_to_medical_record(consultation):
medical_record_id = frappe.db.sql("select name from `tabPatient Medical Record` where reference_name=%s",(consultation.name))
medical_record_id = frappe.db.sql("select name from `tabPatient Medical Record` where reference_name=%s", (consultation.name))
if(medical_record_id[0][0]):
subject = set_subject_field(consultation)
frappe.db.set_value("Patient Medical Record",medical_record_id[0][0],"subject",subject)
frappe.db.set_value("Patient Medical Record", medical_record_id[0][0], "subject", subject)
def delete_medical_record(consultation):
frappe.db.sql("""delete from `tabPatient Medical Record` where reference_name = %s""", (consultation.name))
def set_subject_field(consultation):
subject = "No Diagnosis "

View File

@@ -9,6 +9,7 @@ frappe.ui.form.on('Healthcare Settings', {
filters: {
'account_type': 'Receivable',
'company': d.company,
'is_group': 0
}
};
});
@@ -18,6 +19,7 @@ frappe.ui.form.on('Healthcare Settings', {
filters: {
'root_type': 'Income',
'company': d.company,
'is_group': 0
}
};
});

View File

@@ -114,7 +114,7 @@
"columns": 0,
"fieldname": "patient_name",
"fieldtype": "Data",
"hidden": 1,
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
@@ -185,7 +185,7 @@
"label": "Gender",
"length": 0,
"no_copy": 0,
"options": "\nMale\nFemale",
"options": "\nMale\nFemale\nOther",
"permlevel": 0,
"precision": "",
"print_hide": 1,
@@ -1388,7 +1388,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-10-05 12:14:57.078823",
"modified": "2017-11-22 14:32:27.994634",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Lab Test",

View File

@@ -126,7 +126,7 @@
"label": "Gender",
"length": 0,
"no_copy": 0,
"options": "\nMale\nFemale",
"options": "\nMale\nFemale\nOther",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -185,7 +185,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "DOB",
"label": "Date of birth",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -353,37 +353,6 @@
"set_only_once": 1,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Company",
"length": 0,
"no_copy": 0,
"options": "Company",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@@ -884,7 +853,7 @@
"label": "Marital Status",
"length": 0,
"no_copy": 0,
"options": "Single\nMarried\nDivorced\nWidow",
"options": "\nSingle\nMarried\nDivorced\nWidow",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -1274,7 +1243,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 50,
"modified": "2017-10-04 17:41:03.219934",
"modified": "2017-11-24 12:39:33.061005",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Patient",

View File

@@ -69,7 +69,10 @@ class Patient(Document):
frappe.db.set_value("Patient", self.name, "disabled", 0)
send_registration_sms(self)
if(frappe.get_value("Healthcare Settings", None, "registration_fee")>0):
sales_invoice = make_invoice(self.name, self.company)
company = frappe.defaults.get_user_default('company')
if not company:
company = frappe.db.get_value("Global Defaults", None, "default_company")
sales_invoice = make_invoice(self.name, company)
sales_invoice.save(ignore_permissions=True)
return {'invoice': sales_invoice.name}
@@ -110,7 +113,7 @@ def make_invoice(patient, company):
return sales_invoice
@frappe.whitelist()
def get_patient_detail(patient, company=None):
def get_patient_detail(patient):
patient_dict = frappe.db.sql("""select * from tabPatient where name=%s""", (patient), as_dict=1)
if not patient_dict:
frappe.throw("Patient not found")

View File

@@ -11,8 +11,8 @@ def get_data():
'items': ['Patient Appointment', 'Consultation']
},
{
'label': _('Lab Tests'),
'items': ['Lab Test']
'label': _('Lab Tests and Vital Signs'),
'items': ['Lab Test', 'Vital Signs']
}
]
}

View File

@@ -25,6 +25,14 @@ frappe.ui.form.on('Patient Appointment', {
frm.add_custom_button(__('Cancel'), function() {
btn_update_status(frm, "Cancelled");
});
frm.add_custom_button(__("Consultation"),function(){
btn_create_consultation(frm);
},"Create");
frm.add_custom_button(__('Vital Signs'), function() {
btn_create_vital_signs(frm);
},"Create");
}
if(frm.doc.status == "Scheduled" && !frm.doc.__islocal){
frm.add_custom_button(__('Cancel'), function() {
@@ -177,6 +185,7 @@ var btn_create_vital_signs = function (frm) {
}
frappe.route_options = {
"patient": frm.doc.patient,
"appointment": frm.doc.name,
};
frappe.new_doc("Vital Signs");
};

View File

@@ -165,6 +165,100 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fieldname": "patient_name",
"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": "Patient Name",
"length": 0,
"no_copy": 0,
"options": "patient.patient_name",
"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,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "patient_sex",
"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": "Gender",
"length": 0,
"no_copy": 1,
"options": "patient.sex",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "patient_age",
"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": "Patient Age",
"length": 0,
"no_copy": 0,
"options": "",
"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,
@@ -440,158 +534,6 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_2",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "patient_details",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Patient Details",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "patient",
"fieldname": "patient_name",
"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": "Patient Name",
"length": 0,
"no_copy": 0,
"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,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "patient_sex",
"fieldtype": "Data",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Gender",
"length": 0,
"no_copy": 1,
"options": "patient.sex",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "patient_age",
"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": "Patient Age",
"length": 0,
"no_copy": 0,
"options": "",
"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,
@@ -756,7 +698,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-10-25 23:33:36.060803",
"modified": "2017-11-22 16:32:57.240736",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Patient Appointment",

View File

@@ -56,7 +56,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Patient",
"length": 0,
@@ -174,7 +174,7 @@
"ignore_xss_filter": 1,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Subject",
"length": 0,
@@ -236,7 +236,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Datetime",
"length": 0,
@@ -266,7 +266,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Reference DocType",
"length": 0,
@@ -297,7 +297,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Reference Name",
"length": 0,
@@ -389,7 +389,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-10-04 16:09:55.597866",
"modified": "2017-11-15 12:48:59.945615",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Patient Medical Record",
@@ -425,6 +425,7 @@
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "patient",
"track_changes": 1,
"track_seen": 1
}

View File

@@ -9,6 +9,7 @@ frappe.ui.form.on('Physician', {
filters: {
'root_type': 'Income',
'company': d.company,
'is_group': 0
}
};
});

View File

@@ -33,7 +33,7 @@ frappe.ui.form.on('Physician Schedule', {
while(cur_time < end_time) {
let to_time = cur_time.clone().add(values.duration, 'minutes');
if(to_time < end_time) {
if(to_time <= end_time) {
// add a new timeslot
frm.add_child('time_slots', {

View File

@@ -43,6 +43,37 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "patient_name",
"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": "Patient Name",
"length": 0,
"no_copy": 0,
"options": "patient.patient_name",
"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,
@@ -751,7 +782,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-10-04 16:08:36.340607",
"modified": "2017-11-22 17:31:16.620650",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Vital Signs",

View File

@@ -38,9 +38,8 @@ class TestEmployeeLoan(unittest.TestCase):
self.assertEquals(employee_loan.total_interest_payable, 22712)
self.assertEquals(employee_loan.total_payment, 302712)
def create_loan_type(loan_name, maximum_loan_amount, rate_of_interest):
if not frappe.db.get_value("Loan Type", loan_name):
if not frappe.db.exists("Loan Type", loan_name):
frappe.get_doc({
"doctype": "Loan Type",
"loan_name": loan_name,
@@ -49,6 +48,7 @@ def create_loan_type(loan_name, maximum_loan_amount, rate_of_interest):
}).insert()
def create_employee_loan(employee, loan_type, loan_amount, repayment_method, repayment_periods):
create_loan_type(loan_type, 500000, 8.4)
if not frappe.db.get_value("Employee Loan", {"employee":employee}):
employee_loan = frappe.new_doc("Employee Loan")
employee_loan.update({

View File

@@ -190,23 +190,28 @@ class ProcessPayroll(Document):
def format_as_links(self, salary_slip):
return ['<a href="#Form/Salary Slip/{0}">{0}</a>'.format(salary_slip)]
def get_total_salary_and_loan_amounts(self):
def get_loan_details(self):
"""
Get total loan principal, loan interest and salary amount from submitted salary slip based on selected criteria
Get loan details from submitted salary slip based on selected criteria
"""
cond = self.get_filter_condition()
totals = frappe.db.sql("""
select sum(principal_amount) as total_principal_amount, sum(interest_amount) as total_interest_amount,
sum(total_loan_repayment) as total_loan_repayment, sum(rounded_total) as rounded_total from `tabSalary Slip` t1
return frappe.db.sql(""" select eld.employee_loan_account,
eld.interest_income_account, eld.principal_amount, eld.interest_amount, eld.total_payment
from
`tabSalary Slip` t1, `tabSalary Slip Loan` eld
where
t1.docstatus = 1 and t1.name = eld.parent and start_date >= %s and end_date <= %s %s
""" % ('%s', '%s', cond), (self.start_date, self.end_date), as_dict=True) or []
def get_total_salary_amount(self):
"""
Get total salary amount from submitted salary slip based on selected criteria
"""
cond = self.get_filter_condition()
totals = frappe.db.sql(""" select sum(rounded_total) as rounded_total from `tabSalary Slip` t1
where t1.docstatus = 1 and start_date >= %s and end_date <= %s %s
""" % ('%s', '%s', cond), (self.start_date, self.end_date), as_dict=True)
return totals[0]
def get_loan_accounts(self):
loan_accounts = frappe.get_all("Employee Loan", fields=["employee_loan_account", "interest_income_account"],
filters = {"company": self.company, "docstatus":1})
if loan_accounts:
return loan_accounts[0]
return totals and totals[0] or None
def get_salary_component_account(self, salary_component):
account = frappe.db.get_value("Salary Component Account",
@@ -257,8 +262,7 @@ class ProcessPayroll(Document):
earnings = self.get_salary_component_total(component_type = "earnings") or {}
deductions = self.get_salary_component_total(component_type = "deductions") or {}
default_payroll_payable_account = self.get_default_payroll_payable_account()
loan_amounts = self.get_total_salary_and_loan_amounts()
loan_accounts = self.get_loan_accounts()
loan_details = self.get_loan_details()
jv_name = ""
precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency")
@@ -294,18 +298,18 @@ class ProcessPayroll(Document):
})
# Employee loan
if loan_amounts.total_loan_repayment:
for data in loan_details:
accounts.append({
"account": loan_accounts.employee_loan_account,
"credit_in_account_currency": loan_amounts.total_principal_amount
"account": data.employee_loan_account,
"credit_in_account_currency": data.principal_amount
})
accounts.append({
"account": loan_accounts.interest_income_account,
"credit_in_account_currency": loan_amounts.total_interest_amount,
"account": data.interest_income_account,
"credit_in_account_currency": data.interest_amount,
"cost_center": self.cost_center,
"project": self.project
})
payable_amount -= flt(loan_amounts.total_loan_repayment, precision)
payable_amount -= flt(data.total_payment, precision)
# Payable amount
accounts.append({
@@ -327,11 +331,11 @@ class ProcessPayroll(Document):
def make_payment_entry(self):
self.check_permission('write')
total_salary_amount = self.get_total_salary_and_loan_amounts()
total_salary_amount = self.get_total_salary_amount()
default_payroll_payable_account = self.get_default_payroll_payable_account()
precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency")
if total_salary_amount.rounded_total:
if total_salary_amount and total_salary_amount.rounded_total:
journal_entry = frappe.new_doc('Journal Entry')
journal_entry.voucher_type = 'Bank Entry'
journal_entry.user_remark = _('Payment of salary from {0} to {1}')\

View File

@@ -6,9 +6,9 @@ import unittest
import erpnext
import frappe
from frappe.utils import nowdate
from erpnext.hr.doctype.process_payroll.process_payroll import get_end_date
from dateutil.relativedelta import relativedelta
from erpnext.accounts.utils import get_fiscal_year, getdate, nowdate
from erpnext.hr.doctype.process_payroll.process_payroll import get_start_end_dates, get_end_date
class TestProcessPayroll(unittest.TestCase):
def test_process_payroll(self):
@@ -19,22 +19,9 @@ class TestProcessPayroll(unittest.TestCase):
if not frappe.db.get_value('Salary Component Account',
{'parent': data.name, 'company': erpnext.get_default_company()}, 'name'):
get_salary_component_account(data.name)
payment_account = frappe.get_value('Account',
{'account_type': 'Cash', 'company': erpnext.get_default_company(),'is_group':0}, "name")
if not frappe.db.get_value("Salary Slip", {"start_date": "2016-11-01", "end_date": "2016-11-30"}):
process_payroll = frappe.get_doc("Process Payroll", "Process Payroll")
process_payroll.company = erpnext.get_default_company()
process_payroll.start_date = "2016-11-01"
process_payroll.end_date = "2016-11-30"
process_payroll.payment_account = payment_account
process_payroll.posting_date = nowdate()
process_payroll.payroll_frequency = "Monthly"
process_payroll.create_salary_slips()
process_payroll.submit_salary_slips()
if process_payroll.get_sal_slip_list(ss_status = 1):
r = process_payroll.make_payment_entry()
make_process_payroll()
def test_get_end_date(self):
self.assertEqual(get_end_date('2017-01-01', 'monthly'), {'end_date': '2017-01-31'})
@@ -45,7 +32,99 @@ class TestProcessPayroll(unittest.TestCase):
self.assertEqual(get_end_date('2020-02-15', 'bimonthly'), {'end_date': ''})
self.assertEqual(get_end_date('2017-02-15', 'monthly'), {'end_date': '2017-03-14'})
self.assertEqual(get_end_date('2017-02-15', 'daily'), {'end_date': '2017-02-15'})
def test_employee_loan(self):
from erpnext.hr.doctype.salary_structure.test_salary_structure import (make_employee,
make_salary_structure)
from erpnext.hr.doctype.employee_loan.test_employee_loan import create_employee_loan
branch = "Test Employee Branch"
employee = make_employee("test_employee@loan.com")
company = erpnext.get_default_company()
holiday_list = make_holiday("test holiday for loan")
if not frappe.db.exists('Salary Component', 'Basic Salary'):
frappe.get_doc({
'doctype': 'Salary Component',
'salary_component': 'Basic Salary',
'salary_component_abbr': 'BS',
'type': 'Earning',
'accounts': [{
'company': company,
'default_account': frappe.db.get_value('Account',
{'company': company, 'root_type': 'Expense', 'account_type': ''}, 'name')
}]
}).insert()
if not frappe.db.get_value('Salary Component Account',
{'parent': 'Basic Salary', 'company': company}):
salary_component = frappe.get_doc('Salary Component', 'Basic Salary')
salary_component.append('accounts', {
'company': company,
'default_account': 'Salary - WP'
})
company_doc = frappe.get_doc('Company', company)
if not company_doc.default_payroll_payable_account:
company_doc.default_payroll_payable_account = frappe.db.get_value('Account',
{'company': company, 'root_type': 'Liability', 'account_type': ''}, 'name')
company_doc.save()
if not frappe.db.exists('Branch', branch):
frappe.get_doc({
'doctype': 'Branch',
'branch': branch
}).insert()
employee_doc = frappe.get_doc('Employee', employee)
employee_doc.branch = branch
employee_doc.holiday_list = holiday_list
employee_doc.save()
employee_loan = create_employee_loan(employee,
"Personal Loan", 280000, "Repay Over Number of Periods", 20)
employee_loan.repay_from_salary = 1
employee_loan.submit()
salary_strcture = "Test Salary Structure for Loan"
if not frappe.db.exists('Salary Structure', salary_strcture):
salary_strcture = make_salary_structure(salary_strcture, [{
'employee': employee,
'from_date': '2017-01-01',
'base': 30000
}])
salary_strcture = frappe.get_doc('Salary Structure', salary_strcture)
salary_strcture.set('earnings', [{
'salary_component': 'Basic Salary',
'abbr': 'BS',
'amount_based_on_formula':1,
'formula': 'base*.5'
}])
salary_strcture.save()
dates = get_start_end_dates('Monthly', nowdate())
make_process_payroll(start_date=dates.start_date,
end_date=dates.end_date, branch=branch)
name = frappe.db.get_value('Salary Slip',
{'posting_date': nowdate(), 'employee': employee}, 'name')
salary_slip = frappe.get_doc('Salary Slip', name)
for row in salary_slip.loans:
if row.employee_loan == employee_loan.name:
interest_amount = (280000 * 8.4)/(12*100)
principal_amount = employee_loan.monthly_repayment_amount - interest_amount
self.assertEqual(row.interest_amount, interest_amount)
self.assertEqual(row.principal_amount, principal_amount)
self.assertEqual(row.total_payment,
interest_amount + principal_amount)
if salary_slip.docstatus == 0:
frappe.delete_doc('Salary Slip', name)
employee_loan.cancel()
frappe.delete_doc('Employee Loan', employee_loan.name)
def get_salary_component_account(sal_comp):
company = erpnext.get_default_company()
@@ -63,4 +142,54 @@ def create_account(company):
"parent_account": "Indirect Expenses - " + frappe.db.get_value('Company', company, 'abbr'),
"company": company
}).insert()
return salary_account
return salary_account
def make_process_payroll(**args):
args = frappe._dict(args)
process_payroll = frappe.get_doc("Process Payroll", "Process Payroll")
process_payroll.company = erpnext.get_default_company()
process_payroll.start_date = args.start_date or "2016-11-01"
process_payroll.end_date = args.end_date or "2016-11-30"
process_payroll.payment_account = get_payment_account()
process_payroll.posting_date = nowdate()
process_payroll.payroll_frequency = "Monthly"
process_payroll.branch = args.branch or None
process_payroll.create_salary_slips()
process_payroll.submit_salary_slips()
if process_payroll.get_sal_slip_list(ss_status = 1):
r = process_payroll.make_payment_entry()
return process_payroll
def get_payment_account():
return frappe.get_value('Account',
{'account_type': 'Cash', 'company': erpnext.get_default_company(),'is_group':0}, "name")
def make_holiday(holiday_list_name):
if not frappe.db.exists('Holiday List', holiday_list_name):
current_fiscal_year = get_fiscal_year(nowdate(), as_dict=True)
dt = getdate(nowdate())
new_year = dt + relativedelta(month=01, day=01, year=dt.year)
republic_day = dt + relativedelta(month=01, day=26, year=dt.year)
test_holiday = dt + relativedelta(month=02, day=02, year=dt.year)
frappe.get_doc({
'doctype': 'Holiday List',
'from_date': current_fiscal_year.year_start_date,
'to_date': current_fiscal_year.year_end_date,
'holiday_list_name': holiday_list_name,
'holidays': [{
'holiday_date': new_year,
'description': 'New Year'
}, {
'holiday_date': republic_day,
'description': 'Republic Day'
}, {
'holiday_date': test_holiday,
'description': 'Test Holiday'
}]
}).insert()
return holiday_list_name

View File

@@ -1293,7 +1293,68 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "principal_amount",
"fieldname": "loans",
"fieldtype": "Table",
"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": "Employee Loan",
"length": 0,
"no_copy": 0,
"options": "Salary Slip Loan",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_43",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0",
"fieldname": "total_principal_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -1302,7 +1363,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Principal Amount",
"label": "Total Principal Amount",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
@@ -1324,7 +1385,8 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "interest_amount",
"default": "0",
"fieldname": "total_interest_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -1333,7 +1395,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Interest Amount",
"label": "Total Interest Amount",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
@@ -1355,7 +1417,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_48",
"fieldname": "column_break_45",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -1384,6 +1446,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0",
"fieldname": "total_loan_repayment",
"fieldtype": "Currency",
"hidden": 0,
@@ -1604,7 +1667,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-11-10 18:40:33.817074",
"modified": "2017-11-13 23:55:37.504856",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Slip",

View File

@@ -375,15 +375,34 @@ class SalarySlip(TransactionBase):
self.precision("net_pay") if disable_rounded_total else 0)
def set_loan_repayment(self):
employee_loan = frappe.db.sql("""select sum(principal_amount) as principal_amount, sum(interest_amount) as interest_amount,
sum(total_payment) as total_loan_repayment from `tabRepayment Schedule`
where payment_date between %s and %s and parent in (select name from `tabEmployee Loan`
where employee = %s and repay_from_salary = 1 and docstatus = 1)""",
(self.start_date, self.end_date, self.employee), as_dict=True)
if employee_loan:
self.principal_amount = employee_loan[0].principal_amount
self.interest_amount = employee_loan[0].interest_amount
self.total_loan_repayment = employee_loan[0].total_loan_repayment
self.set('loans', [])
self.total_loan_repayment = 0
self.total_interest_amount = 0
self.total_principal_amount = 0
for loan in self.get_employee_loan_details():
self.append('loans', {
'employee_loan': loan.name,
'total_payment': loan.total_payment,
'interest_amount': loan.interest_amount,
'principal_amount': loan.principal_amount,
'employee_loan_account': loan.employee_loan_account,
'interest_income_account': loan.interest_income_account
})
self.total_loan_repayment += loan.total_payment
self.total_interest_amount += loan.interest_amount
self.total_principal_amount += loan.principal_amount
def get_employee_loan_details(self):
return frappe.db.sql("""select rps.principal_amount, rps.interest_amount, el.name,
rps.total_payment, el.employee_loan_account, el.interest_income_account
from
`tabRepayment Schedule` as rps, `tabEmployee Loan` as el
where
el.name = rps.parent and rps.payment_date between %s and %s and
el.repay_from_salary = 1 and el.docstatus = 1 and el.employee = %s""",
(self.start_date, self.end_date, self.employee), as_dict=True) or []
def on_submit(self):
if self.net_pay < 0:

View File

@@ -0,0 +1,256 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2017-11-08 12:51:12.834479",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "employee_loan",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Employee Loan",
"length": 0,
"no_copy": 0,
"options": "Employee Loan",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "employee_loan_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Employee Loan Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"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,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "interest_income_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Interest Income Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"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,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_4",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "principal_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Principal Amount",
"length": 0,
"no_copy": 0,
"options": "",
"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,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "interest_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Interest Amount",
"length": 0,
"no_copy": 0,
"options": "",
"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,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "total_payment",
"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": "Total Payment",
"length": 0,
"no_copy": 0,
"options": "",
"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
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-11-13 23:59:47.237689",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Slip Loan",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
class SalarySlipLoan(Document):
pass

View File

@@ -17,11 +17,9 @@ class TestSalaryStructure(unittest.TestCase):
def setUp(self):
self.make_holiday_list()
frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Structure Test Holiday List")
make_earning_salary_component(["Basic Salary", "Special Allowance", "HRA"])
make_deduction_salary_component(["Professional Tax", "TDS"])
make_employee("test_employee@salary.com")
make_employee("test_employee_2@salary.com")
def make_holiday_list(self):
if not frappe.db.get_value("Holiday List", "Salary Structure Test Holiday List"):
holiday_list = frappe.get_doc({
@@ -33,7 +31,7 @@ class TestSalaryStructure(unittest.TestCase):
}).insert()
holiday_list.get_weekly_off_dates()
holiday_list.save()
def test_amount_totals(self):
sal_slip = frappe.get_value("Salary Slip", {"employee_name":"test_employee@salary.com"})
if not sal_slip:
@@ -64,7 +62,7 @@ class TestSalaryStructure(unittest.TestCase):
for row in salary_structure.deductions:
self.assertFalse(("\n" in row.formula) or ("\n" in row.condition))
def make_employee(user):
if not frappe.db.get_value("User", user):
frappe.get_doc({
@@ -74,7 +72,6 @@ def make_employee(user):
"new_password": "password",
"roles": [{"doctype": "Has Role", "role": "Employee"}]
}).insert()
if not frappe.db.get_value("Employee", {"user_id": user}):
emp = frappe.get_doc({
@@ -95,7 +92,7 @@ def make_employee(user):
return emp.name
else:
return frappe.get_value("Employee", {"employee_name":user}, "name")
def make_salary_slip_from_salary_structure(employee):
sal_struct = make_salary_structure('Salary Structure Sample')
sal_slip = make_salary_slip(sal_struct, employee = employee)
@@ -106,22 +103,21 @@ def make_salary_slip_from_salary_structure(employee):
sal_slip.insert()
sal_slip.submit()
return sal_slip
def make_salary_structure(sal_struct):
def make_salary_structure(sal_struct, employees=None):
if not frappe.db.exists('Salary Structure', sal_struct):
frappe.get_doc({
"doctype": "Salary Structure",
"name": sal_struct,
"company": erpnext.get_default_company(),
"employees": get_employee_details(),
"employees": employees or get_employee_details(),
"earnings": get_earnings_component(),
"deductions": get_deductions_component(),
"payroll_frequency": "Monthly",
"payment_account": frappe.get_value('Account', {'account_type': 'Cash', 'company': erpnext.get_default_company(),'is_group':0}, "name")
}).insert()
return sal_struct
return sal_struct
def get_employee_details():
return [{"employee": frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"),
"base": 25000,
@@ -136,8 +132,11 @@ def get_employee_details():
"idx": 2
}
]
def get_earnings_component():
def get_earnings_component():
make_earning_salary_component(["Basic Salary", "Special Allowance", "HRA"])
make_deduction_salary_component(["Professional Tax", "TDS"])
return [
{
"salary_component": 'Basic Salary',
@@ -167,7 +166,7 @@ def get_earnings_component():
"idx": 4
},
]
def get_deductions_component():
return [
{
@@ -191,5 +190,4 @@ def get_deductions_component():
"formula": 'base*.1',
"idx": 3
}
]
]

View File

@@ -441,6 +441,8 @@ class ProductionOrder(Document):
for item in sorted(item_dict.values(), key=lambda d: d['idx']):
self.append('required_items', {
'item_code': item.item_code,
'item_name': item.item_name,
'description': item.description,
'required_qty': item.qty,
'source_warehouse': item.source_warehouse or item.default_warehouse
})

View File

@@ -15,62 +15,66 @@ def get_item_list(prod_list, filters):
out = []
#Add a row for each item/qty
for prod_order in prod_list:
prod_details = frappe.db.get_value("Production Order", prod_order.name,
["bom_no", "source_warehouse", "qty", "produced_qty"], as_dict=1)
for prod_details in prod_list:
desc = frappe.db.get_value("BOM", prod_details.bom_no, "description")
item_list = frappe.db.sql("""SELECT
bom_item.item_code as item_code,
ifnull(ledger.actual_qty*bom.quantity/bom_item.stock_qty,0) as build_qty
FROM
`tabBOM` as bom, `tabBOM Item` AS bom_item
LEFT JOIN `tabBin` AS ledger
ON bom_item.item_code = ledger.item_code
AND ledger.warehouse = ifnull(%(warehouse)s,%(filterhouse)s)
WHERE
bom.name = bom_item.parent
and bom.name = %(bom)s
GROUP BY
bom_item.item_code""",
{"bom": prod_details.bom_no, "warehouse": prod_details.source_warehouse,
"filterhouse": filters.warehouse}, as_dict=1)
stock_qty = 0
count = 0
buildable_qty = prod_details.qty
for item in item_list:
count = count + 1
if item.build_qty >= (prod_details.qty - prod_details.produced_qty):
stock_qty = stock_qty + 1
elif buildable_qty >= item.build_qty:
buildable_qty = item.build_qty
if count == stock_qty:
build = "Y"
else:
build = "N"
row = frappe._dict({
"production_order": prod_order.name,
"status": prod_order.status,
"req_items": cint(count),
"instock": stock_qty,
"description": desc,
"bom_no": prod_details.bom_no,
"qty": prod_details.qty,
"buildable_qty": buildable_qty,
"ready_to_build": build
})
out.append(row)
for prod_item_details in frappe.db.get_values("Production Order Item",
{"parent": prod_details.name}, ["item_code", "source_warehouse"], as_dict=1):
item_list = frappe.db.sql("""SELECT
bom_item.item_code as item_code,
ifnull(ledger.actual_qty*bom.quantity/bom_item.stock_qty,0) as build_qty
FROM
`tabBOM` as bom, `tabBOM Item` AS bom_item
LEFT JOIN `tabBin` AS ledger
ON bom_item.item_code = ledger.item_code
AND ledger.warehouse = ifnull(%(warehouse)s,%(filterhouse)s)
WHERE
bom.name = bom_item.parent
and bom_item.item_code = %(item_code)s
and bom.name = %(bom)s
GROUP BY
bom_item.item_code""",
{"bom": prod_details.bom_no, "warehouse": prod_item_details.source_warehouse,
"filterhouse": filters.warehouse, "item_code": prod_item_details.item_code}, as_dict=1)
stock_qty = 0
count = 0
buildable_qty = prod_details.qty
for item in item_list:
count = count + 1
if item.build_qty >= (prod_details.qty - prod_details.produced_qty):
stock_qty = stock_qty + 1
elif buildable_qty >= item.build_qty:
buildable_qty = item.build_qty
if count == stock_qty:
build = "Y"
else:
build = "N"
row = frappe._dict({
"production_order": prod_details.name,
"status": prod_details.status,
"req_items": cint(count),
"instock": stock_qty,
"description": desc,
"source_warehouse": prod_item_details.source_warehouse,
"item_code": prod_item_details.item_code,
"bom_no": prod_details.bom_no,
"qty": prod_details.qty,
"buildable_qty": buildable_qty,
"ready_to_build": build
})
out.append(row)
return out
def get_production_orders():
out = frappe.get_all("Production Order", filters={"docstatus": 1, "status": ( "!=","Completed")}, fields=["name","status"], order_by='name')
out = frappe.get_all("Production Order", filters={"docstatus": 1, "status": ( "!=","Completed")},
fields=["name","status", "bom_no", "qty", "produced_qty"], order_by='name')
return out
def get_columns():
@@ -93,6 +97,18 @@ def get_columns():
"options": "",
"width": 230
}, {
"fieldname": "item_code",
"label": "Item Code",
"fieldtype": "Link",
"options": "Item",
"width": 110
},{
"fieldname": "source_warehouse",
"label": "Source Warehouse",
"fieldtype": "Link",
"options": "Warehouse",
"width": 110
},{
"fieldname": "qty",
"label": "Qty to Build",
"fieldtype": "Data",

View File

@@ -458,3 +458,9 @@ erpnext.patches.v9_0.copy_old_fees_field_data
erpnext.patches.v9_0.set_pos_profile_name
erpnext.patches.v9_0.remove_non_existing_warehouse_from_stock_settings
execute:frappe.delete_doc_if_exists("DocType", "Program Fee")
erpnext.patches.v9_0.update_employee_loan_details
erpnext.patches.v9_2.delete_healthcare_domain_default_items
erpnext.patches.v9_2.rename_translated_domains_in_en
erpnext.patches.v9_2.repost_reserved_qty_for_production
erpnext.patches.v9_2.remove_company_from_patient
erpnext.patches.v9_2.set_item_name_in_production_order

View File

@@ -0,0 +1,24 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
frappe.reload_doc('hr', 'doctype', 'salary_slip_loan')
frappe.reload_doc('hr', 'doctype', 'salary_slip')
for data in frappe.db.sql(""" select name,
start_date, end_date, total_loan_repayment
from
`tabSalary Slip`
where
docstatus < 2 and ifnull(total_loan_repayment, 0) > 0""", as_dict=1):
salary_slip = frappe.get_doc('Salary Slip', data.name)
salary_slip.set_loan_repayment()
if salary_slip.total_loan_repayment == data.total_loan_repayment:
for row in salary_slip.loans:
row.db_update()
salary_slip.db_update()

View File

View File

@@ -0,0 +1,16 @@
import frappe
from frappe.utils import getdate
def execute():
domain_settings = frappe.get_doc('Domain Settings')
active_domains = [d.domain for d in domain_settings.active_domains]
if "Healthcare" not in active_domains:
items = ["TTT", "MCH", "LDL", "GTT", "HDL", "BILT", "BILD", "BP", "BS"]
for item_code in items:
try:
item = frappe.db.get_value("Item", {"item_code": item_code}, ["name", "creation"], as_dict=1)
if item and getdate(item.creation) >= getdate("2017-11-10"):
frappe.delete_doc("Item", item.name)
except:
pass

View File

@@ -0,0 +1,5 @@
import frappe
def execute():
if 'company' in frappe.db.get_table_columns("Patient"):
frappe.db.sql("alter table `tabPatient` drop column company")

View File

@@ -0,0 +1,31 @@
import frappe
from frappe import _
def execute():
language = frappe.get_single("System Settings").language
if language and language.startswith('en'): return
frappe.local.lang = language
all_domains = frappe.get_hooks("domains")
for domain in all_domains:
translated_domain = _(domain, lang=language)
if frappe.db.exists("Domain", translated_domain):
frappe.rename_doc("Domain", translated_domain, domain, ignore_permissions=True, merge=True)
domain_settings = frappe.get_single("Domain Settings")
active_domains = [d.domain for d in domain_settings.active_domains]
try:
for domain in active_domains:
domain = frappe.get_doc("Domain", domain)
domain.setup_domain()
if int(frappe.db.get_single_value('System Settings', 'setup_complete')):
domain.setup_sidebar_items()
domain.setup_desktop_icons()
domain.set_default_portal_role()
except frappe.LinkValidationError:
pass

View File

@@ -0,0 +1,7 @@
import frappe
def execute():
bins = frappe.db.sql("select name from `tabBin` where reserved_qty_for_production > 0")
for d in bins:
bin_doc = frappe.get_doc("Bin", d[0])
bin_doc.update_reserved_qty_for_production()

View File

@@ -0,0 +1,11 @@
import frappe
def execute():
frappe.db.sql("""
update `tabBOM Item` bom, `tabProduction Order Item` po_item
set po_item.item_name = bom.item_name,
po_item.description = bom.description
where po_item.item_code = bom.item_code
and (po_item.item_name is null or po_item.description is null)
""")

View File

@@ -231,8 +231,16 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
},
callback: function(r) {
if(!r.exc) {
me.frm.set_value("taxes", r.message);
me.calculate_taxes_and_totals();
frappe.run_serially([
() => {
// directly set in doc, so as not to call triggers
me.frm.doc.taxes_and_charges = r.message.taxes_and_charges;
// set taxes table
me.frm.set_value("taxes", r.message.taxes);
},
() => me.calculate_taxes_and_totals()
]);
}
}
});
@@ -935,19 +943,27 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
return;
}
if (me.in_apply_price_list == true) return;
me.in_apply_price_list = true;
return this.frm.call({
method: "erpnext.stock.get_item_details.apply_price_list",
args: { args: args },
callback: function(r) {
if (!r.exc) {
me.in_apply_price_list = true;
me.frm.set_value("price_list_currency", r.message.parent.price_list_currency);
me.frm.set_value("plc_conversion_rate", r.message.parent.plc_conversion_rate);
me.in_apply_price_list = false;
frappe.run_serially([
() => me.frm.set_value("price_list_currency", r.message.parent.price_list_currency),
() => me.frm.set_value("plc_conversion_rate", r.message.parent.plc_conversion_rate),
() => {
if(args.items.length) {
me._set_values_for_item_list(r.message.children);
}
},
() => { me.in_apply_price_list = false; }
]);
if(args.items.length) {
me._set_values_for_item_list(r.message.children);
}
} else {
me.in_apply_price_list = false;
}
}
});
@@ -1112,11 +1128,11 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
filters: {'item': item.item_code}
}
} else {
filters = {
let filters = {
'item_code': item.item_code,
'posting_date': me.frm.doc.posting_date || frappe.datetime.nowdate(),
}
if(item.warehouse) filters["warehouse"] = item.warehouse
if (item.warehouse) filters["warehouse"] = item.warehouse
return {
query : "erpnext.controllers.queries.get_batch_no",

View File

@@ -103,7 +103,7 @@ class SalesOrder(SellingController):
def validate_delivery_date(self):
if self.order_type == 'Sales':
if not self.delivery_date:
self.delivery_date = max([d.delivery_date for d in self.get("items")])
self.delivery_date = max([d.delivery_date for d in self.get("items") if d.delivery_date])
if self.delivery_date:
for d in self.get("items"):

View File

@@ -53,8 +53,6 @@ erpnext.pos.PointOfSale = class PointOfSale {
() => this.setup_pos_profile(),
() => this.make_new_invoice(),
() => {
frappe.timeout(1);
this.make_items();
this.bind_events();
frappe.dom.unfreeze();
},
@@ -323,16 +321,20 @@ erpnext.pos.PointOfSale = class PointOfSale {
make_new_invoice() {
return frappe.run_serially([
() => this.make_sales_invoice_frm(),
() => {
if (this.cart) {
this.cart.frm = this.frm;
this.cart.reset();
} else {
this.make_cart();
}
this.toggle_editing(true);
}
this.make_sales_invoice_frm()
.then(() => this.set_pos_profile_data())
.then(() => {
if (this.cart) {
this.cart.frm = this.frm;
this.cart.reset();
} else {
this.make_items();
this.make_cart();
}
this.toggle_editing(true);
})
},
]);
}
@@ -359,12 +361,29 @@ erpnext.pos.PointOfSale = class PointOfSale {
if(!frm.doc.company) {
frm.set_value('company', pos_profile.company);
}
frm.set_value('is_pos', 1);
frm.meta.default_print_format = 'POS Invoice';
frm.doc.is_pos = 1;
return frm;
}
}
set_pos_profile_data() {
return new Promise(resolve => {
return this.frm.call({
doc: this.frm.doc,
method: "set_missing_values",
}).then((r) => {
if(!r.exc) {
this.frm.script_manager.trigger("update_stock");
frappe.model.set_default_values(this.frm.doc);
this.frm.cscript.calculate_taxes_and_totals();
this.frm.meta.default_print_format = r.message.print_format || 'POS Invoice';
}
resolve();
})
})
}
prepare_menu() {
var me = this;
this.page.clear_menu();
@@ -392,9 +411,6 @@ erpnext.pos.PointOfSale = class PointOfSale {
if(this.frm.doc.docstatus !== 1) return;
this.page.set_secondary_action(__("Print"), () => {
if (this.pos_profile && this.pos_profile.print_format_for_online) {
this.frm.meta.default_print_format = this.pos_profile.print_format_for_online;
}
this.frm.print_preview.printit(true);
});
@@ -991,7 +1007,7 @@ class POSItems {
this.get_items({search_value: search_term, item_group })
.then(({ items, serial_no, batch_no, barcode }) => {
if (search_term) {
if (search_term && !barcode) {
this.search_index[search_term] = items;
}
@@ -1264,6 +1280,16 @@ class Payment {
$(this.dialog.body).find('.input-with-feedback').focusin(function() {
me.numpad.reset_value();
me.fieldname = $(this).prop('dataset').fieldname;
if (me.frm.doc.outstanding_amount > 0 &&
!in_list(['write_off_amount', 'change_amount'], me.fieldname)) {
me.frm.doc.payments.forEach((data) => {
if (data.mode_of_payment == me.fieldname && !data.amount) {
me.dialog.set_value(me.fieldname,
me.frm.doc.outstanding_amount / me.frm.doc.conversion_rate);
return;
}
})
}
});
}
@@ -1403,4 +1429,4 @@ class Payment {
this.dialog.set_value("paid_amount", this.frm.doc.paid_amount);
this.dialog.set_value("outstanding_amount", this.frm.doc.outstanding_amount);
}
}
}

View File

@@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:party_type",
@@ -13,6 +14,7 @@
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -43,17 +45,17 @@
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"in_create": 1,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-02-20 13:25:04.456818",
"modified": "2017-11-23 17:46:27.075001",
"modified_by": "Administrator",
"module": "Setup",
"name": "Party Type",
@@ -64,8 +66,8 @@
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"create": 0,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
@@ -78,14 +80,14 @@
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
"write": 0
},
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"create": 0,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
@@ -98,14 +100,14 @@
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
"write": 0
},
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"create": 0,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
@@ -118,10 +120,10 @@
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
"write": 0
}
],
"quick_entry": 1,
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 1,

View File

@@ -0,0 +1,23 @@
/* eslint-disable */
// rename this file from _test_[name] to test_[name] to activate
// and remove above this line
QUnit.test("test: Party Type", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(1);
frappe.run_serially([
// insert a new Party Type
() => frappe.tests.make('Party Type', [
// values to be set
{key: 'value'}
]),
() => {
assert.equal(cur_frm.doc.key, 'value');
},
() => done()
]);
});

View File

@@ -3,7 +3,7 @@
frappe.ui.form.on('Sales Partner', {
refresh: function(frm) {
frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Sales Person'}
frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Sales Partner'}
if(frm.doc.__islocal){
hide_field(['address_html', 'contact_html', 'address_contacts']);

View File

@@ -14,12 +14,12 @@ default_lead_sources = ["Existing Customer", "Reference", "Advertisement",
def install(country=None):
records = [
# domains
{ 'doctype': 'Domain', 'domain': _('Distribution')},
{ 'doctype': 'Domain', 'domain': _('Manufacturing')},
{ 'doctype': 'Domain', 'domain': _('Retail')},
{ 'doctype': 'Domain', 'domain': _('Services')},
{ 'doctype': 'Domain', 'domain': _('Education')},
{ 'doctype': 'Domain', 'domain': _('Healthcare')},
{ 'doctype': 'Domain', 'domain': 'Distribution'},
{ 'doctype': 'Domain', 'domain': 'Manufacturing'},
{ 'doctype': 'Domain', 'domain': 'Retail'},
{ 'doctype': 'Domain', 'domain': 'Services'},
{ 'doctype': 'Domain', 'domain': 'Education'},
{ 'doctype': 'Domain', 'domain': 'Healthcare'},
# Setup Progress
{'doctype': "Setup Progress", "actions": [

View File

@@ -40,7 +40,7 @@ def setup_complete(args=None):
frappe.local.message_log = []
domain_settings = frappe.get_single('Domain Settings')
domain_settings.set_active_domains([_(args.get('domain'))])
domain_settings.set_active_domains([args.get('domain')])
frappe.db.commit()
login_as_first_user(args)
@@ -186,10 +186,6 @@ def set_defaults(args):
hr_settings.emp_created_by = "Naming Series"
hr_settings.save()
domain_settings = frappe.get_doc("Domain Settings")
domain_settings.append('active_domains', dict(domain=_(args.get('domain'))))
domain_settings.save()
def create_feed_and_todo():
"""update Activity feed and create todo for creation of item, customer, vendor"""
add_info_comment(**{

View File

@@ -90,7 +90,7 @@ class Bin(Document):
self.set_projected_qty()
self.db_set('reserved_qty_for_production', self.reserved_qty_for_production)
self.db_set('reserved_qty_for_production', flt(self.reserved_qty_for_production))
self.db_set('projected_qty', self.projected_qty)

View File

@@ -157,7 +157,7 @@ class Item(WebsiteGenerator):
def make_route(self):
if not self.route:
return cstr(frappe.db.get_value('Item Group', self.item_group,
'route')) + '/' + self.scrub(self.item_name + '-' + random_string(5))
'route')) + '/' + self.scrub((self.item_name if self.item_name else self.item_code) + '-' + random_string(5))
def validate_website_image(self):
"""Validate if the website image is a public file"""

View File

@@ -14,8 +14,6 @@ frappe.ui.form.on('Stock Entry', {
]
}
});
// },
// onload_post_render: function(frm) {
frm.set_query('batch_no', 'items', function(doc, cdt, cdn) {
var item = locals[cdt][cdn];
@@ -40,9 +38,8 @@ frappe.ui.form.on('Stock Entry', {
}
}
});
},
refresh: function(frm) {
if(!frm.doc.docstatus) {
frm.add_custom_button(__('Make Material Request'), function() {
@@ -73,10 +70,12 @@ frappe.ui.form.on('Stock Entry', {
frm.trigger("toggle_display_account_head");
}
},
purpose: function(frm) {
frm.fields_dict.items.grid.refresh();
frm.cscript.toggle_related_fields(frm.doc);
},
company: function(frm) {
if(frm.doc.company) {
var company_doc = frappe.get_doc(":Company", frm.doc.company);
@@ -86,6 +85,7 @@ frappe.ui.form.on('Stock Entry', {
frm.trigger("toggle_display_account_head");
}
},
set_serial_no: function(frm, cdt, cdn) {
var d = frappe.model.get_doc(cdt, cdn);
if(!d.item_code && !d.s_warehouse && !d.qty) return;
@@ -104,20 +104,142 @@ frappe.ui.form.on('Stock Entry', {
}
});
},
toggle_display_account_head: function(frm) {
var enabled = erpnext.is_perpetual_inventory_enabled(frm.doc.company);
frm.fields_dict["items"].grid.set_column_disp(["cost_center", "expense_account"], enabled);
}
},
set_basic_rate: function(frm, cdt, cdn, callback) {
const item = locals[cdt][cdn];
item.transfer_qty = flt(item.qty) * flt(item.conversion_factor);
const args = {
'item_code' : item.item_code,
'posting_date' : frm.doc.posting_date,
'posting_time' : frm.doc.posting_time,
'warehouse' : cstr(item.s_warehouse) || cstr(item.t_warehouse),
'serial_no ' : item.serial_no,
'company' : frm.doc.company,
'qty' : item.s_warehouse ? -1*flt(item.transfer_qty) : flt(item.transfer_qty)
};
frappe.call({
method: "erpnext.stock.utils.get_incoming_rate",
args: {
args: args
},
callback: function(r) {
frappe.model.set_value(cdt, cdn, 'basic_rate', r.message);
frm.events.calculate_basic_amount(frm, item);
if (callback) {
callback();
}
}
})
},
get_warehouse_details: function(frm, cdt, cdn, callback) {
var child = locals[cdt][cdn];
if(!child.bom_no) {
frappe.call({
method: "erpnext.stock.doctype.stock_entry.stock_entry.get_warehouse_details",
args: {
"args": {
'item_code': child.item_code,
'warehouse': cstr(child.s_warehouse) || cstr(child.t_warehouse),
'transfer_qty': child.transfer_qty,
'serial_no': child.serial_no,
'qty': child.s_warehouse ? -1* child.transfer_qty : child.transfer_qty,
'posting_date': frm.doc.posting_date,
'posting_time': frm.doc.posting_time
}
},
callback: function(r) {
if (!r.exc) {
$.extend(child, r.message);
frm.events.calculate_basic_amount(frm, child);
}
if (callback) {
callback();
}
}
});
}
},
calculate_basic_amount: function(frm, item) {
item.basic_amount = flt(flt(item.transfer_qty) * flt(item.basic_rate),
precision("basic_amount", item));
frm.events.calculate_amount(frm);
},
calculate_amount: function(frm) {
frm.events.calculate_total_additional_costs(frm);
const total_basic_amount = frappe.utils.sum(
(frm.doc.items || []).map(function(i) { return i.t_warehouse ? flt(i.basic_amount) : 0; })
);
for (let i in frm.doc.items) {
let item = frm.doc.items[i];
if (item.t_warehouse && total_basic_amount) {
item.additional_cost = (flt(item.basic_amount) / total_basic_amount) * frm.doc.total_additional_costs;
} else {
item.additional_cost = 0;
}
item.amount = flt(item.basic_amount + flt(item.additional_cost),
precision("amount", item));
item.valuation_rate = flt(flt(item.basic_rate)
+ (flt(item.additional_cost) / flt(item.transfer_qty)),
precision("valuation_rate", item));
}
refresh_field('items');
},
calculate_total_additional_costs: function(frm) {
const total_additional_costs = frappe.utils.sum(
(frm.doc.additional_costs || []).map(function(c) { return flt(c.amount); })
);
frm.set_value("total_additional_costs",
flt(total_additional_costs, precision("total_additional_costs")));
},
})
frappe.ui.form.on('Stock Entry Detail', {
qty: function(frm, cdt, cdn) {
frm.events.set_serial_no(frm, cdt, cdn);
frm.events.set_basic_rate(frm, cdt, cdn, () => {
frm.events.set_serial_no(frm, cdt, cdn);
});
},
conversion_factor: function(frm, cdt, cdn) {
frm.events.set_basic_rate(frm, cdt, cdn);
},
s_warehouse: function(frm, cdt, cdn) {
frm.events.set_serial_no(frm, cdt, cdn);
frm.events.get_warehouse_details(frm, cdt, cdn, () => {
frm.events.set_serial_no(frm, cdt, cdn);
});
},
t_warehouse: function(frm, cdt, cdn) {
frm.events.get_warehouse_details(frm, cdt, cdn);
},
basic_rate: function(frm, cdt, cdn) {
var item = locals[cdt][cdn];
frm.events.calculate_basic_amount(frm, item);
},
barcode: function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
if (d.barcode) {
@@ -132,6 +254,7 @@ frappe.ui.form.on('Stock Entry Detail', {
});
}
},
uom: function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
if(d.uom && d.item_code){
@@ -150,6 +273,7 @@ frappe.ui.form.on('Stock Entry Detail', {
});
}
},
item_code: function(frm, cdt, cdn) {
var d = locals[cdt][cdn];
if(d.item_code) {
@@ -191,7 +315,7 @@ frappe.ui.form.on('Stock Entry Detail', {
frappe.ui.form.on('Landed Cost Taxes and Charges', {
amount: function(frm) {
frm.events.calculate_amount();
frm.events.calculate_amount(frm);
}
});
@@ -330,12 +454,6 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
}
},
qty: function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
d.transfer_qty = flt(d.qty) * flt(d.conversion_factor);
this.calculate_basic_amount(d);
},
production_order: function() {
var me = this;
this.toggle_enable_bom();
@@ -434,88 +552,6 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
erpnext.setup_serial_no();
},
basic_rate: function(doc, cdt, cdn) {
var item = frappe.model.get_doc(cdt, cdn);
this.calculate_basic_amount(item);
},
s_warehouse: function(doc, cdt, cdn) {
this.get_warehouse_details(doc, cdt, cdn)
},
t_warehouse: function(doc, cdt, cdn) {
this.get_warehouse_details(doc, cdt, cdn)
},
get_warehouse_details: function(doc, cdt, cdn) {
var me = this;
var d = locals[cdt][cdn];
if(!d.bom_no) {
frappe.call({
method: "erpnext.stock.doctype.stock_entry.stock_entry.get_warehouse_details",
args: {
"args": {
'item_code': d.item_code,
'warehouse': cstr(d.s_warehouse) || cstr(d.t_warehouse),
'transfer_qty': d.transfer_qty,
'serial_no': d.serial_no,
'qty': d.s_warehouse ? -1* d.qty : d.qty,
'posting_date': this.frm.doc.posting_date,
'posting_time': this.frm.doc.posting_time
}
},
callback: function(r) {
if (!r.exc) {
$.extend(d, r.message);
me.calculate_basic_amount(d);
}
}
});
}
},
calculate_basic_amount: function(item) {
item.basic_amount = flt(flt(item.transfer_qty) * flt(item.basic_rate),
precision("basic_amount", item));
this.calculate_amount();
},
calculate_amount: function() {
this.calculate_total_additional_costs();
var total_basic_amount = frappe.utils.sum(
(this.frm.doc.items || []).map(function(i) { return i.t_warehouse ? flt(i.basic_amount) : 0; })
);
for (var i in this.frm.doc.items) {
var item = this.frm.doc.items[i];
if (item.t_warehouse && total_basic_amount) {
item.additional_cost = (flt(item.basic_amount) / total_basic_amount) * this.frm.doc.total_additional_costs;
} else {
item.additional_cost = 0;
}
item.amount = flt(item.basic_amount + flt(item.additional_cost),
precision("amount", item));
item.valuation_rate = flt(flt(item.basic_rate)
+ (flt(item.additional_cost) / flt(item.transfer_qty)),
precision("valuation_rate", item));
}
refresh_field('items');
},
calculate_total_additional_costs: function() {
var total_additional_costs = frappe.utils.sum(
(this.frm.doc.additional_costs || []).map(function(c) { return flt(c.amount); })
);
this.frm.set_value("total_additional_costs", flt(total_additional_costs, precision("total_additional_costs")));
},
toggle_related_fields: function(doc) {
this.frm.toggle_enable("from_warehouse", doc.purpose!='Material Receipt');
this.frm.toggle_enable("to_warehouse", doc.purpose!='Material Issue');

View File

@@ -517,7 +517,7 @@ class StockEntry(StockController):
args['posting_date'] = self.posting_date
args['posting_time'] = self.posting_time
stock_and_rate = args.get('warehouse') and get_warehouse_details(args) or {}
stock_and_rate = get_warehouse_details(args) if args.get('warehouse') else {}
ret.update(stock_and_rate)
# automatically select batch for outgoing item

View File

@@ -96,16 +96,6 @@ def get_item_details(args):
return out
# print(frappe._dict({
# 'has_serial_no' : out.has_serial_no,
# 'has_batch_no' : out.has_batch_no
# }))
# return frappe._dict({
# 'has_serial_no' : out.has_serial_no,
# 'has_batch_no' : out.has_batch_no
# })
def process_args(args):
if isinstance(args, basestring):
args = json.loads(args)
@@ -373,11 +363,11 @@ def get_pos_profile_item_details(company, args, pos_profile=None):
@frappe.whitelist()
def get_pos_profile(company):
pos_profile = frappe.db.sql("""select * from `tabPOS Profile` where user = %s
and company = %s""", (frappe.session['user'], company), as_dict=1)
and company = %s and ifnull(disabled,0) != 1""", (frappe.session['user'], company), as_dict=1)
if not pos_profile:
pos_profile = frappe.db.sql("""select * from `tabPOS Profile`
where ifnull(user,'') = '' and company = %s""", company, as_dict=1)
where ifnull(user,'') = '' and company = %s and ifnull(disabled,0) != 1""", company, as_dict=1)
return pos_profile and pos_profile[0] or None
@@ -532,8 +522,6 @@ def get_default_bom(item_code=None):
bom = frappe.db.get_value("BOM", {"docstatus": 1, "is_default": 1, "is_active": 1, "item": item_code})
if bom:
return bom
else:
frappe.throw(_("No default BOM exists for Item {0}").format(item_code))
def get_valuation_rate(item_code, warehouse=None):
item = frappe.get_doc("Item", item_code)