Merge pull request #6366 from rohitwaghchaure/opportunity_enhancement
[Enhancement] Make supplier quotation from opportunity
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 38cef31..adc30e2 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -302,6 +302,9 @@
asset.save()
def make_gl_entries(self, repost_future_gle=True):
+ if not self.grand_total:
+ return
+
self.auto_accounting_for_stock = \
cint(frappe.defaults.get_global_default("auto_accounting_for_stock"))
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 4886dea..47f1a5c 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -462,26 +462,41 @@
frappe.ui.form.on('Sales Invoice', {
setup: function(frm){
frm.fields_dict["timesheets"].grid.get_field("time_sheet").get_query = function(doc, cdt, cdn){
- return {
- filters: [
- ["Timesheet", "status", "in", ["Submitted", "Payslip"]]
- ]
+ return{
+ query: "erpnext.projects.doctype.timesheet.timesheet.get_timesheet",
+ filters: {'project': doc.project}
}
}
- }
-})
+ },
-frappe.ui.form.on('Sales Invoice Timesheet', {
- time_sheet: function(frm){
+ project: function(frm){
frm.call({
- method: "calculate_billing_amount_from_timesheet",
+ method: "add_timesheet_data",
doc: frm.doc,
callback: function(r, rt) {
- refresh_field('total_billing_amount')
+ refresh_field(['timesheets'])
}
})
}
})
-cur_frm.add_fetch("time_sheet", "total_billing_hours", "billing_hours");
-cur_frm.add_fetch("time_sheet", "total_billing_amount", "billing_amount");
\ No newline at end of file
+frappe.ui.form.on('Sales Invoice Timesheet', {
+ time_sheet: function(frm, cdt, cdn){
+ var d = locals[cdt][cdn];
+ frappe.call({
+ method: "erpnext.projects.doctype.timesheet.timesheet.get_timesheet_data",
+ args: {
+ 'name': d.time_sheet,
+ 'project': frm.doc.project || null
+ },
+ callback: function(r, rt) {
+ if(r.message){
+ data = r.message;
+ frappe.model.set_value(cdt, cdn, "billing_hours", data.billing_hours);
+ frappe.model.set_value(cdt, cdn, "billing_amount", data.billing_amount);
+ frappe.model.set_value(cdt, cdn, "timesheet_detail", data.timesheet_detail);
+ }
+ }
+ })
+ }
+})
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index 562eaaa..99b4793 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -342,6 +342,34 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fieldname": "project",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 1,
+ "in_list_view": 0,
+ "label": "Project",
+ "length": 0,
+ "no_copy": 0,
+ "oldfieldname": "project_name",
+ "oldfieldtype": "Link",
+ "options": "Project",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 0,
@@ -2878,34 +2906,6 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "project",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 1,
- "in_list_view": 0,
- "label": "Project",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "project_name",
- "oldfieldtype": "Link",
- "options": "Project",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "col_break23",
"fieldtype": "Column Break",
"hidden": 0,
@@ -3867,7 +3867,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2016-08-31 15:47:32.064861",
+ "modified": "2016-09-08 09:05:02.895682",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 8b2113a..2a0077a 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -14,6 +14,7 @@
from erpnext.controllers.selling_controller import SellingController
from erpnext.accounts.utils import get_account_currency
from erpnext.stock.doctype.delivery_note.delivery_note import update_billed_amount_based_on_so
+from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data
from erpnext.accounts.doctype.asset.depreciation \
import get_disposal_account_and_cost_center, get_gl_entries_on_asset_disposal
@@ -84,7 +85,7 @@
self.validate_multiple_billing("Delivery Note", "dn_detail", "amount", "items")
self.update_packing_list()
self.set_billing_hours_and_amount()
- self.calculate_billing_amount_from_timesheet()
+ self.update_timesheet_billing_for_project()
def before_save(self):
set_account_for_mode_of_payment(self)
@@ -221,11 +222,21 @@
for d in self.timesheets:
if d.time_sheet:
timesheet = frappe.get_doc("Timesheet", d.time_sheet)
- timesheet.sales_invoice = sales_invoice
+ self.update_time_sheet_detail(timesheet, d, sales_invoice)
+ timesheet.calculate_total_amounts()
+ timesheet.calculate_percentage_billed()
timesheet.flags.ignore_validate_update_after_submit = True
timesheet.set_status()
timesheet.save()
+ def update_time_sheet_detail(self, timesheet, args, sales_invoice):
+ for data in timesheet.time_logs:
+ if (self.project and args.timesheet_detail == data.name) or \
+ (not self.project and not data.sales_invoice) or \
+ (not sales_invoice and data.sales_invoice == self.name):
+ data.sales_invoice = sales_invoice
+ if self.project: return
+
def on_update(self):
self.set_paid_amount()
@@ -450,13 +461,32 @@
def set_billing_hours_and_amount(self):
for timesheet in self.timesheets:
ts_doc = frappe.get_doc('Timesheet', timesheet.time_sheet)
- if not timesheet.billing_hours and ts_doc.total_billing_hours:
- timesheet.billing_hours = ts_doc.total_billing_hours
+ if not timesheet.billing_hours and ts_doc.total_billable_hours:
+ timesheet.billing_hours = ts_doc.total_billable_hours
- if not timesheet.billing_amount and ts_doc.total_billing_amount:
- timesheet.billing_amount = ts_doc.total_billing_amount
+ if not timesheet.billing_amount and ts_doc.total_billable_amount:
+ timesheet.billing_amount = ts_doc.total_billable_amount
- def calculate_billing_amount_from_timesheet(self):
+ def update_timesheet_billing_for_project(self):
+ if not self.timesheets and self.project:
+ self.add_timesheet_data()
+ else:
+ self.calculate_billing_amount_for_timesheet()
+
+ def add_timesheet_data(self):
+ self.set('timesheets', [])
+ if self.project:
+ for data in get_projectwise_timesheet_data(self.project):
+ self.append('timesheets', {
+ 'time_sheet': data.parent,
+ 'billing_hours': data.billing_hours,
+ 'billing_amount': data.billing_amt,
+ 'timesheet_detail': data.name
+ })
+
+ self.calculate_billing_amount_for_timesheet()
+
+ def calculate_billing_amount_for_timesheet(self):
total_billing_amount = 0.0
for data in self.timesheets:
if data.billing_amount:
@@ -500,6 +530,8 @@
throw(_("Delivery Note {0} is not submitted").format(d.delivery_note))
def make_gl_entries(self, repost_future_gle=True):
+ if not self.grand_total:
+ return
gl_entries = self.get_gl_entries()
if gl_entries:
diff --git a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
index 25dd3cb..1191ea7 100644
--- a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
+++ b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
@@ -14,6 +14,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "time_sheet",
"fieldtype": "Link",
"hidden": 0,
@@ -40,6 +41,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "billing_hours",
"fieldtype": "Float",
"hidden": 0,
@@ -65,6 +67,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "billing_amount",
"fieldtype": "Currency",
"hidden": 0,
@@ -85,6 +88,32 @@
"search_index": 0,
"set_only_once": 0,
"unique": 0
+ },
+ {
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "timesheet_detail",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Timesheet Detail",
+ "length": 0,
+ "no_copy": 1,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
}
],
"hide_heading": 0,
@@ -97,7 +126,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2016-08-22 21:32:55.504103",
+ "modified": "2016-09-09 14:01:04.095775",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Timesheet",
diff --git a/erpnext/accounts/print_format/payment_receipt_voucher/payment_receipt_voucher.html b/erpnext/accounts/print_format/payment_receipt_voucher/payment_receipt_voucher.html
index a4f8fa6..f2e65d3 100644
--- a/erpnext/accounts/print_format/payment_receipt_voucher/payment_receipt_voucher.html
+++ b/erpnext/accounts/print_format/payment_receipt_voucher/payment_receipt_voucher.html
@@ -2,7 +2,7 @@
<div class="page-break">
{%- if not doc.get("print_heading") and not doc.get("select_print_heading")
and doc.set("select_print_heading", _("Payment Receipt Note")) -%}{%- endif -%}
- {{ add_header(0, 1, doc, letter_head, no_letterhead) }}
+ {{ add_header(0, 1, doc, letter_head, no_letterhead, print_settings) }}
{%- for label, value in (
(_("Received On"), frappe.utils.formatdate(doc.voucher_date)),
diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index 0917428..97d2cdb 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -12,20 +12,12 @@
"""Get a list of dict {"from_date": from_date, "to_date": to_date, "key": key, "label": label}
Periodicity can be (Yearly, Quarterly, Monthly)"""
- from_fy_start_end_date = frappe.db.get_value("Fiscal Year", from_fiscal_year, ["year_start_date", "year_end_date"])
- to_fy_start_end_date = frappe.db.get_value("Fiscal Year", to_fiscal_year, ["year_start_date", "year_end_date"])
-
- if not from_fy_start_end_date:
- frappe.throw(_("Start Year {0} not found.").format(from_fiscal_year))
-
- if not to_fy_start_end_date:
- frappe.throw(_("End Year {0} not found.").format(to_fiscal_year))
+ fiscal_year = get_fiscal_year_data(from_fiscal_year, to_fiscal_year)
+ validate_fiscal_year(fiscal_year, from_fiscal_year, to_fiscal_year)
# start with first day, so as to avoid year to_dates like 2-April if ever they occur]
- year_start_date = getdate(from_fy_start_end_date[0])
- year_end_date = getdate(to_fy_start_end_date[1])
-
- validate_fiscal_year(year_start_date, year_end_date)
+ year_start_date = getdate(fiscal_year.year_start_date)
+ year_end_date = getdate(fiscal_year.year_end_date)
months_to_add = {
"Yearly": 12,
@@ -46,7 +38,7 @@
to_date = add_months(start_date, months_to_add)
start_date = to_date
-
+
if to_date == get_first_day(to_date):
# if to_date is the first day, get the last day of previous month
to_date = add_days(to_date, -1)
@@ -85,8 +77,16 @@
return period_list
-def validate_fiscal_year(start_date, end_date):
- if date_diff(end_date, start_date) <= 0:
+def get_fiscal_year_data(from_fiscal_year, to_fiscal_year):
+ fiscal_year = frappe.db.sql("""select min(year_start_date) as year_start_date,
+ max(year_end_date) as year_end_date from `tabFiscal Year` where
+ name between %(from_fiscal_year)s and %(to_fiscal_year)s""",
+ {'from_fiscal_year': from_fiscal_year, 'to_fiscal_year': to_fiscal_year}, as_dict=1)
+
+ return fiscal_year[0] if fiscal_year else {}
+
+def validate_fiscal_year(fiscal_year, from_fiscal_year, to_fiscal_year):
+ if not fiscal_year.get('year_start_date') and not fiscal_year.get('year_end_date'):
frappe.throw(_("End Year cannot be before Start Year"))
def get_months(start_date, end_date):
@@ -142,10 +142,9 @@
for period in period_list:
# check if posting date is within the period
- fiscal_year = get_date_fiscal_year(entry.posting_date)
if entry.posting_date <= period.to_date:
if (accumulated_values or entry.posting_date >= period.from_date) and \
- (fiscal_year == period.to_date_fiscal_year or not ignore_accumulated_values_for_fy):
+ (entry.fiscal_year == period.to_date_fiscal_year or not ignore_accumulated_values_for_fy):
d[period.key] = d.get(period.key, 0.0) + flt(entry.debit) - flt(entry.credit)
if entry.posting_date < period_list[0].year_start_date:
@@ -294,7 +293,7 @@
additional_conditions = get_additional_conditions(from_date, ignore_closing_entries, filters)
- gl_entries = frappe.db.sql("""select posting_date, account, debit, credit, is_opening from `tabGL Entry`
+ gl_entries = frappe.db.sql("""select posting_date, account, debit, credit, is_opening, fiscal_year from `tabGL Entry`
where company=%(company)s
{additional_conditions}
and posting_date <= %(to_date)s
diff --git a/erpnext/config/desktop.py b/erpnext/config/desktop.py
index 2b8dce0..c5a1320 100644
--- a/erpnext/config/desktop.py
+++ b/erpnext/config/desktop.py
@@ -60,7 +60,7 @@
"link": "List/Lead"
},
{
- "module_name": "Profit and Loss Statment",
+ "module_name": "Profit and Loss Statement",
"_doctype": "Account",
"color": "#3498db",
"icon": "octicon octicon-repo",
diff --git a/erpnext/config/schools.py b/erpnext/config/schools.py
index ce0c5dc..6acf081 100644
--- a/erpnext/config/schools.py
+++ b/erpnext/config/schools.py
@@ -39,6 +39,10 @@
},
{
"type": "doctype",
+ "name": "Student Admission"
+ },
+ {
+ "type": "doctype",
"name": "Program Enrollment"
},
{
diff --git a/erpnext/demo/user/projects.py b/erpnext/demo/user/projects.py
index 505ccfd..9802447 100644
--- a/erpnext/demo/user/projects.py
+++ b/erpnext/demo/user/projects.py
@@ -22,7 +22,7 @@
ts = make_timesheet(employee, simulate = True, billable = 1,
activity_type=get_random("Activity Type"), project=data.project, task =data.name)
- if flt(ts.total_billing_amount) > 0.0:
+ if flt(ts.total_billable_amount) > 0.0:
make_sales_invoice_for_timesheet(ts.name)
frappe.db.commit()
diff --git a/erpnext/docs/assets/img/setup/email/auto-email-1.png b/erpnext/docs/assets/img/setup/email/auto-email-1.png
new file mode 100644
index 0000000..be4cc43
--- /dev/null
+++ b/erpnext/docs/assets/img/setup/email/auto-email-1.png
Binary files differ
diff --git a/erpnext/docs/assets/img/setup/email/auto-email-2.png b/erpnext/docs/assets/img/setup/email/auto-email-2.png
new file mode 100644
index 0000000..e8cb9f3
--- /dev/null
+++ b/erpnext/docs/assets/img/setup/email/auto-email-2.png
Binary files differ
diff --git a/erpnext/docs/assets/img/setup/email/auto-email-3.png b/erpnext/docs/assets/img/setup/email/auto-email-3.png
new file mode 100644
index 0000000..2656c24
--- /dev/null
+++ b/erpnext/docs/assets/img/setup/email/auto-email-3.png
Binary files differ
diff --git a/erpnext/docs/assets/img/setup/email/auto-email-4.png b/erpnext/docs/assets/img/setup/email/auto-email-4.png
new file mode 100644
index 0000000..81d0d41
--- /dev/null
+++ b/erpnext/docs/assets/img/setup/email/auto-email-4.png
Binary files differ
diff --git a/erpnext/docs/user/manual/en/projects/timesheet/timesheet-against-project.md b/erpnext/docs/user/manual/en/projects/timesheet/timesheet-against-project.md
index 7460a4b..a8b2a6b 100644
--- a/erpnext/docs/user/manual/en/projects/timesheet/timesheet-against-project.md
+++ b/erpnext/docs/user/manual/en/projects/timesheet/timesheet-against-project.md
@@ -8,6 +8,9 @@
To bill Customer based on Timesheet, check "Is Billable" in the Timesheet created against Project and Task. To learn more about billing Customer from Timesheet, click [here]({{docs_base_url}}/user/manual/en/projects/timesheet/sales-invoice-from-timesheet.html).
+User can also make invoice against timesheet by selecting the project on the invoice. System will fetch the records from the timesheet based on selected project, for mode detail check below video
+<iframe width="560" height="315" src="https://www.youtube.com/embed/hVAjtOFFhDI" frameborder="0" allowfullscreen></iframe>
+
####Project Costing
When creating Timesheet, Employee will have to select an Activity Type. For each Activity Type, you can create an Activity Cost master. In the Activity Cost, Billing Rate and Costing rate is defined for each Employee.
diff --git a/erpnext/docs/user/manual/en/setting-up/email/email-reports.md b/erpnext/docs/user/manual/en/setting-up/email/email-reports.md
new file mode 100644
index 0000000..723fa27
--- /dev/null
+++ b/erpnext/docs/user/manual/en/setting-up/email/email-reports.md
@@ -0,0 +1,37 @@
+# Email Reports at Regular Intervals
+
+You can setup **Auto Email Report** to send reports at regular intervals. These must be saved reports of any type (Report Builder, Script or Query Report).
+
+You can find Auto Email Report at
+
+> Setup > Email > Auto Email Report
+
+Or just type "Auto Email Report" on the Search bar.
+
+### Example
+
+#### Step 1
+
+Select the Report, the user for which you want to create this report (permissions will apply for this user), the email ids where you want this report emailed and the frequency of the report.
+
+<img class="screenshot" alt="Make Auto Email Report" src="{{docs_base_url}}/assets/img/setup/email/auto-email-1.png">
+
+#### Step 2
+
+If your report has filters, you will see a table with the filters
+
+Step 1. Select the Report, the user for which you want to create this report. Permissions will apply for this user
+
+<img class="screenshot" alt="With Filters" src="{{docs_base_url}}/assets/img/setup/email/auto-email-1.png">
+
+Click on the table to edit the table
+
+<img class="screenshot" alt="Edit Filters" src="{{docs_base_url}}/assets/img/setup/email/auto-email-3.png">
+
+#### Test
+
+You can also test the report by clicking on "Download" or "Send Now"
+
+Here is an example of the email you will receive for a report
+
+<img class="screenshot" alt="Report by Email" src="{{docs_base_url}}/assets/img/setup/email/auto-email-4.png">
diff --git a/erpnext/docs/user/manual/en/setting-up/email/index.txt b/erpnext/docs/user/manual/en/setting-up/email/index.txt
index 86e75c2..7ca1b94 100644
--- a/erpnext/docs/user/manual/en/setting-up/email/index.txt
+++ b/erpnext/docs/user/manual/en/setting-up/email/index.txt
@@ -1,5 +1,5 @@
email-account
email-alerts
email-digest
-sending-email
-setting-up-email
+email-reports
+sending-email
\ No newline at end of file
diff --git a/erpnext/docs/user/manual/en/stock/item/index.md b/erpnext/docs/user/manual/en/stock/item/index.md
index 4af8eff..7ca9d4e 100644
--- a/erpnext/docs/user/manual/en/stock/item/index.md
+++ b/erpnext/docs/user/manual/en/stock/item/index.md
@@ -1,4 +1,4 @@
-An Item is your companys' product or a service. The term Item is applicable to your core products as well as your raw materials. It can be a product or service that you buy/sell from your customers/ suppliers. ERPNext allows you to manage all sorts of items like raw-materials, sub-assemblies, finished goods, item variants and service items.
+An Item is your companys' product or a service. The term Item is applicable to things (products or services) you sell as well as raw materials or components of products yet to be produced (before they can be sold to customers). An Item can be a phyical product or a service that you buy/sell from your customers/suppliers. ERPNext allows you to manage all sorts of items like raw-materials, sub-assemblies, finished goods, item variants and service items.
ERPNext is optimized for itemized management of your sales and purchase. If you are in services, you can create an Item for each services that your offer. Completing the Item Master is very essential for successful implementation of ERPNext.
diff --git a/erpnext/docs/user/manual/en/stock/item/item-variants.md b/erpnext/docs/user/manual/en/stock/item/item-variants.md
index cee3ab1..cdca6ed 100644
--- a/erpnext/docs/user/manual/en/stock/item/item-variants.md
+++ b/erpnext/docs/user/manual/en/stock/item/item-variants.md
@@ -1,10 +1,10 @@
-A Item Variant is a different version of a Item, such as differing sizes or differing colours.
-Without Item variants, you would have to treat the small, medium and large versions of a t-shirt as three separate Items;
-Item variants let you treat the small, medium and large versions of a t-shirt as variations of the same Item.
+A Item Variant is a version of a Item, such as differing sizes or differing colours (like a _blue_ t-shirt in size _small_ rather then just a t-shirt).
+Without Item variants, you would have to treat the _small, medium_ and _large_ versions of a t-shirt as three separate Items;
+Item variants let you treat the _small, medium_ and _large_ versions of a t-shirt as variations of the one Item 't-shirt'.
-To use Item Variants in ERPNext, create an Item and check 'Has Variants'
+To use Item Variants in ERPNext, create an Item and check 'Has Variants'.
-* The Item shall then be referred as a 'Template'
+* The Item shall then be referred to as a so called 'Template'. Such a Template is not identical to a regular 'Item' any longer. For example it (the Template) can not be used directly in any Transactions (Sales Order, Delivery Note, Purchase Invoice) itself. Only the Variants of an Item (_blue_ t-shirt in size _small)_ can be practically used in such. Therefore it would be ideal to decide whether an item 'Has Variants' or not directly when creating it.
<img class="screenshot" alt="Has Variants" src="{{docs_base_url}}/assets/img/stock/item-has-variants.png">
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index c1cee0c..19e915e 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -45,7 +45,7 @@
fixtures = ["Web Form"]
-website_generators = ["Item Group", "Item", "Sales Partner", "Job Opening"]
+website_generators = ["Item Group", "Item", "Sales Partner", "Job Opening", "Student Admission"]
website_context = {
"favicon": "/assets/erpnext/images/favicon.png",
@@ -89,6 +89,7 @@
}
},
{"from_route": "/jobs", "to_route": "Job Opening"},
+ {"from_route": "/admissions", "to_route": "Student Admission"},
]
portal_menu_items = [
diff --git a/erpnext/hr/doctype/job_opening/job_opening.py b/erpnext/hr/doctype/job_opening/job_opening.py
index 267e7a8..7c42a76 100644
--- a/erpnext/hr/doctype/job_opening/job_opening.py
+++ b/erpnext/hr/doctype/job_opening/job_opening.py
@@ -7,8 +7,6 @@
import frappe
from frappe.website.website_generator import WebsiteGenerator
-from frappe.utils import quoted
-from frappe.utils.user import get_fullname_and_avatar
from frappe import _
class JobOpening(WebsiteGenerator):
@@ -18,9 +16,6 @@
page_title_field = "job_title",
)
- def make_route(self):
- return 'jobs/' + self.scrub(self.job_title)
-
def get_context(self, context):
context.parents = [{'name': 'jobs', 'title': _('All Jobs') }]
diff --git a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.json b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.json
index 8438e07..8d844cd 100644
--- a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.json
+++ b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.json
@@ -702,6 +702,32 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "description": "If checked, Will include non-stock items in the Material Requests.",
+ "fieldname": "create_material_requests_non_stock_request",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Include non-stock items",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
"default": "1",
"depends_on": "",
"description": "If checked, all the children of each production item will be included in the Material Requests.",
@@ -823,4 +849,4 @@
"read_only_onload": 0,
"sort_order": "ASC",
"track_seen": 0
-}
\ No newline at end of file
+}
diff --git a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py
index fd28b54..8521b5e 100644
--- a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py
+++ b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py
@@ -307,7 +307,7 @@
self.get_raw_materials(bom_dict)
return self.get_csv()
- def get_raw_materials(self, bom_dict):
+ def get_raw_materials(self, bom_dict,non_stock_item=0):
""" Get raw materials considering sub-assembly items
{
"item_code": [qty_required, description, stock_uom, min_order_qty]
@@ -326,7 +326,7 @@
from `tabBOM Explosion Item` fb, `tabBOM` bom, `tabItem` item
where bom.name = fb.parent and item.name = fb.item_code
and (item.is_sub_contracted_item = 0 or ifnull(item.default_bom, "")="")
- and item.is_stock_item = 1
+ """ + ("and item.is_stock_item = 1","")[non_stock_item] + """
and fb.docstatus<2 and bom.name=%(bom)s
group by fb.item_code, fb.stock_uom""", {"bom":bom}, as_dict=1):
bom_wise_item_details.setdefault(d.item_code, d)
@@ -334,7 +334,7 @@
# Get all raw materials considering SA items as raw materials,
# so no childs of SA items
bom_wise_item_details = self.get_subitems(bom_wise_item_details, bom,1, \
- self.use_multi_level_bom,self.only_raw_materials, self.include_subcontracted)
+ self.use_multi_level_bom,self.only_raw_materials, self.include_subcontracted,non_stock_item)
for item, item_details in bom_wise_item_details.items():
for so_qty in so_wise_qty:
@@ -343,14 +343,14 @@
self.make_items_dict(item_list)
- def get_subitems(self,bom_wise_item_details, bom, parent_qty, include_sublevel, only_raw, supply_subs):
+ def get_subitems(self,bom_wise_item_details, bom, parent_qty, include_sublevel, only_raw, supply_subs,non_stock_item=0):
for d in frappe.db.sql("""select bom_item.item_code, default_material_request_type,
ifnull(%(parent_qty)s * sum(bom_item.qty/ifnull(bom.quantity, 1)), 0) as qty,
item.is_sub_contracted_item as is_sub_contracted, item.default_bom as default_bom
from `tabBOM Item` bom_item, `tabBOM` bom, tabItem item
where bom.name = bom_item.parent and bom.name = %(bom)s and bom_item.docstatus < 2
and bom_item.item_code = item.name
- and item.is_stock_item = 1
+ """ + ("and item.is_stock_item = 1","")[non_stock_item] + """
group by bom_item.item_code""", {"bom": bom, "parent_qty": parent_qty}, as_dict=1):
if (d.default_material_request_type == "Purchase" and not (d.is_sub_contracted \
and only_raw and include_sublevel)) or (d.default_material_request_type == \
@@ -399,7 +399,7 @@
frappe.throw(_("Please enter Warehouse for which Material Request will be raised"))
bom_dict = self.get_so_wise_planned_qty()
- self.get_raw_materials(bom_dict)
+ self.get_raw_materials(bom_dict,self.create_material_requests_non_stock_request)
if self.item_dict:
self.create_material_request()
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 0706a2c..2b0aaef 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -295,7 +295,8 @@
erpnext.patches.v7_0.rename_time_sheet_doctype
execute:frappe.delete_doc_if_exists("Report", "Customers Not Buying Since Long Time")
erpnext.patches.v7_0.make_is_group_fieldtype_as_check
-execute:frappe.reload_doc('projects', 'doctype', 'timesheet', force=True) #2016-08-23
+execute:frappe.reload_doc('projects', 'doctype', 'timesheet') #2016-09-12
+erpnext.patches.v7_1.rename_field_timesheet
execute:frappe.delete_doc_if_exists("Report", "Employee Holiday Attendance")
execute:frappe.delete_doc_if_exists("DocType", "Payment Tool")
execute:frappe.delete_doc_if_exists("DocType", "Payment Tool Detail")
@@ -323,4 +324,6 @@
erpnext.patches.v7_0.update_status_for_timesheet
erpnext.patches.v7_0.set_party_name_in_payment_entry
erpnext.patches.v7_1.set_student_guardian
-erpnext.patches.v7_0.update_conversion_factor_in_supplier_quotation_item
\ No newline at end of file
+erpnext.patches.v7_0.update_conversion_factor_in_supplier_quotation_item
+erpnext.patches.v7_1.move_sales_invoice_from_parent_to_child_timesheet
+execute:frappe.db.sql("update `tabTimesheet` ts, `tabEmployee` emp set ts.employee_name = emp.employee_name where emp.name = ts.employee and ts.employee_name is null and ts.employee is not null")
diff --git a/erpnext/patches/v7_0/migrate_mode_of_payments_v6_to_v7.py b/erpnext/patches/v7_0/migrate_mode_of_payments_v6_to_v7.py
index 8de92b7..e0e3f70 100644
--- a/erpnext/patches/v7_0/migrate_mode_of_payments_v6_to_v7.py
+++ b/erpnext/patches/v7_0/migrate_mode_of_payments_v6_to_v7.py
@@ -29,8 +29,8 @@
row.db_update()
si_doc.set_paid_amount()
- si_doc.db_set("paid_amount", si_doc.paid_amount)
- si_doc.db_set("base_paid_amount", si_doc.base_paid_amount)
+ si_doc.db_set("paid_amount", si_doc.paid_amount, update_modified = False)
+ si_doc.db_set("base_paid_amount", si_doc.base_paid_amount, update_modified = False)
count +=1
diff --git a/erpnext/patches/v7_0/move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet.py b/erpnext/patches/v7_0/move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet.py
index 695c552..0332955 100644
--- a/erpnext/patches/v7_0/move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet.py
+++ b/erpnext/patches/v7_0/move_timelogbatch_from_salesinvoiceitem_to_salesinvoicetimesheet.py
@@ -3,12 +3,12 @@
def execute():
frappe.reload_doc('accounts', 'doctype', 'sales_invoice')
frappe.reload_doc('accounts', 'doctype', 'sales_invoice_payment')
- for time_sheet in frappe.db.sql(""" select sales_invoice, name, total_billing_amount from `tabTimesheet`
+ for time_sheet in frappe.db.sql(""" select sales_invoice, name, total_billable_amount from `tabTimesheet`
where sales_invoice is not null and docstatus < 2""", as_dict=True):
si_doc = frappe.get_doc('Sales Invoice', time_sheet.sales_invoice)
ts = si_doc.append('timesheets',{})
ts.time_sheet = time_sheet.name
- ts.billing_amount = time_sheet.total_billing_amount
- si_doc.update_time_sheet(time_sheet.sales_invoice)
- si_doc.flags.ignore_validate_update_after_submit = True
- si_doc.save()
\ No newline at end of file
+ ts.billing_amount = time_sheet.total_billable_amount
+ ts.db_update()
+ si_doc.calculate_billing_amount_from_timesheet()
+ si_doc.db_set("total_billing_amount", si_doc.total_billing_amount, update_modified = False)
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/remove_doctypes_and_reports.py b/erpnext/patches/v7_0/remove_doctypes_and_reports.py
index 978c363..0a302b1 100644
--- a/erpnext/patches/v7_0/remove_doctypes_and_reports.py
+++ b/erpnext/patches/v7_0/remove_doctypes_and_reports.py
@@ -1,9 +1,10 @@
import frappe
def execute():
- for doctype in ['Time Log Batch', 'Time Log Batch Detail', 'Time Log']:
- frappe.delete_doc('DocType', doctype)
-
+ if frappe.db.table_exists("Time Log"):
+ frappe.db.sql("""delete from `tabDocType`
+ where name in('Time Log Batch', 'Time Log Batch Detail', 'Time Log')""")
+
report = "Daily Time Log Summary"
if frappe.db.exists("Report", report):
frappe.delete_doc('Report', report)
\ No newline at end of file
diff --git a/erpnext/patches/v7_1/move_sales_invoice_from_parent_to_child_timesheet.py b/erpnext/patches/v7_1/move_sales_invoice_from_parent_to_child_timesheet.py
new file mode 100644
index 0000000..d1ec7c6
--- /dev/null
+++ b/erpnext/patches/v7_1/move_sales_invoice_from_parent_to_child_timesheet.py
@@ -0,0 +1,20 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ frappe.reload_doc('projects', 'doctype', 'timesheet_detail')
+ frappe.reload_doc('accounts', 'doctype', 'sales_invoice_timesheet')
+
+ frappe.db.sql(""" update
+ `tabTimesheet` as ts,
+ (select
+ sum(billing_amount) as billing_amount, sum(billing_hours) as billing_hours, time_sheet
+ from `tabSales Invoice Timesheet` where docstatus = 1 group by time_sheet
+ ) as sit
+ set
+ ts.total_billed_amount = sit.billing_amount, ts.total_billed_hours = sit.billing_hours,
+ ts.per_billed = ((sit.billing_amount * 100)/ts.total_billable_amount)
+ where ts.name = sit.time_sheet and ts.docstatus = 1""")
+
+ frappe.db.sql(""" update `tabTimesheet Detail` tsd, `tabTimesheet` ts set tsd.sales_invoice = ts.sales_invoice
+ where tsd.parent = ts.name and ts.sales_invoice is not null""")
\ No newline at end of file
diff --git a/erpnext/patches/v7_1/rename_field_timesheet.py b/erpnext/patches/v7_1/rename_field_timesheet.py
new file mode 100644
index 0000000..3690a2e
--- /dev/null
+++ b/erpnext/patches/v7_1/rename_field_timesheet.py
@@ -0,0 +1,11 @@
+from __future__ import unicode_literals
+import frappe
+from frappe.model.utils.rename_field import rename_field
+
+def execute():
+ doctype = 'Timesheet'
+ fields_dict = {'total_billing_amount': 'total_billable_amount', 'total_billing_hours': 'total_billable_hours'}
+
+ for old_fieldname, new_fieldname in fields_dict.items():
+ if old_fieldname in frappe.db.get_table_columns(doctype):
+ rename_field(doctype, old_fieldname, new_fieldname)
diff --git a/erpnext/patches/v7_1/update_total_billing_hours.py b/erpnext/patches/v7_1/update_total_billing_hours.py
index a38b88d..b9c9602 100644
--- a/erpnext/patches/v7_1/update_total_billing_hours.py
+++ b/erpnext/patches/v7_1/update_total_billing_hours.py
@@ -5,10 +5,10 @@
frappe.reload_doc('projects', 'doctype', 'timesheet_detail')
frappe.reload_doc('accounts', 'doctype', 'sales_invoice_timesheet')
- frappe.db.sql("""update tabTimesheet set total_billing_hours=total_hours
- where total_billing_amount>0 and docstatus = 1""")
+ frappe.db.sql("""update tabTimesheet set total_billable_hours=total_hours
+ where total_billable_amount>0 and docstatus = 1""")
frappe.db.sql("""update `tabTimesheet Detail` set billing_hours=hours where docstatus < 2""")
- frappe.db.sql(""" update `tabSales Invoice Timesheet` set billing_hours = (select total_billing_hours from `tabTimesheet`
+ frappe.db.sql(""" update `tabSales Invoice Timesheet` set billing_hours = (select total_billable_hours from `tabTimesheet`
where name = time_sheet) where time_sheet is not null""")
\ No newline at end of file
diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js
index 5bb9dd6..ee2db46 100644
--- a/erpnext/projects/doctype/project/project.js
+++ b/erpnext/projects/doctype/project/project.js
@@ -78,7 +78,7 @@
section.on('click', '.time-sheet-link', function() {
var activity_type = $(this).attr('data-activity_type');
frappe.set_route('List', 'Timesheet',
- {'activity_type': activity_type, 'project': frm.doc.name});
+ {'activity_type': activity_type, 'project': frm.doc.name, 'status': ["!=", "Cancelled"]});
});
}
}
diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py
index 8e7e562..369be6d 100644
--- a/erpnext/projects/doctype/timesheet/test_timesheet.py
+++ b/erpnext/projects/doctype/timesheet/test_timesheet.py
@@ -9,20 +9,22 @@
from frappe.utils import now_datetime, nowdate
from erpnext.projects.doctype.timesheet.timesheet import OverlapError
from erpnext.projects.doctype.timesheet.timesheet import make_salary_slip, make_sales_invoice
+from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
class TestTimesheet(unittest.TestCase):
def test_timesheet_billing_amount(self):
salary_structure = make_salary_structure("_T-Employee-0001")
- timesheet = make_timesheet("_T-Employee-0001", True)
+ timesheet = make_timesheet("_T-Employee-0001", simulate = True, billable=1)
self.assertEquals(timesheet.total_hours, 2)
- self.assertEquals(timesheet.total_billing_hours, 2)
+ self.assertEquals(timesheet.total_billable_hours, 2)
self.assertEquals(timesheet.time_logs[0].billing_rate, 50)
self.assertEquals(timesheet.time_logs[0].billing_amount, 100)
+ self.assertEquals(timesheet.total_billable_amount, 100)
def test_salary_slip_from_timesheet(self):
salary_structure = make_salary_structure("_T-Employee-0001")
- timesheet = make_timesheet("_T-Employee-0001", simulate = True)
+ timesheet = make_timesheet("_T-Employee-0001", simulate = True, billable=1)
salary_slip = make_salary_slip(timesheet.name)
salary_slip.submit()
@@ -51,11 +53,20 @@
item.rate = 100
sales_invoice.submit()
-
timesheet = frappe.get_doc('Timesheet', timesheet.name)
self.assertEquals(sales_invoice.total_billing_amount, 100)
self.assertEquals(timesheet.status, 'Billed')
+ def test_timesheet_billing_based_on_project(self):
+ timesheet = make_timesheet("_T-Employee-0001", simulate=True, billable=1, project = '_Test Project', company='_Test Company')
+ sales_invoice = create_sales_invoice(do_not_save=True)
+ sales_invoice.project = '_Test Project'
+ sales_invoice.submit()
+
+ ts = frappe.get_doc('Timesheet', timesheet.name)
+ self.assertEquals(ts.per_billed, 100)
+ self.assertEquals(ts.time_logs[0].sales_invoice, sales_invoice.name)
+
def make_salary_structure(employee):
name = frappe.db.get_value('Salary Structure Employee', {'employee': employee}, 'parent')
if name:
@@ -93,7 +104,7 @@
return salary_structure
-def make_timesheet(employee, simulate=False, billable = 0, activity_type="_Test Activity Type", project=None, task=None):
+def make_timesheet(employee, simulate=False, billable = 0, activity_type="_Test Activity Type", project=None, task=None, company=None):
update_activity_type(activity_type)
timesheet = frappe.new_doc("Timesheet")
timesheet.employee = employee
@@ -105,6 +116,7 @@
timesheet_detail.to_time = timesheet_detail.from_time + datetime.timedelta(hours= timesheet_detail.hours)
timesheet_detail.project = project
timesheet_detail.task = task
+ timesheet_detail.company = company or '_Test Company'
for data in timesheet.get('time_logs'):
if simulate:
diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js
index 42fe005..7d0e71a 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.js
+++ b/erpnext/projects/doctype/timesheet/timesheet.js
@@ -21,6 +21,14 @@
}
}
}
+
+ frm.fields_dict['time_logs'].grid.get_field('project').get_query = function() {
+ return{
+ filters: {
+ 'company': frm.doc.company
+ }
+ }
+ }
},
onload: function(frm){
@@ -31,7 +39,7 @@
refresh: function(frm) {
if(frm.doc.docstatus==1) {
- if(!frm.doc.sales_invoice && frm.doc.total_billing_amount > 0){
+ if(frm.doc.per_billed < 100){
frm.add_custom_button(__("Make Sales Invoice"), function() { frm.trigger("make_invoice") },
"icon-file-alt");
}
@@ -42,8 +50,9 @@
}
}
- if(frm.doc.sales_invoice) {
+ if(frm.doc.per_billed > 0) {
cur_frm.fields_dict["time_logs"].grid.toggle_enable("billing_hours", false);
+ cur_frm.fields_dict["time_logs"].grid.toggle_enable("billable", false);
}
},
@@ -150,19 +159,22 @@
var tl = frm.doc.time_logs || [];
total_working_hr = 0;
total_billing_hr = 0;
- total_billing_amount = 0;
+ total_billable_amount = 0;
total_costing_amount = 0;
for(var i=0; i<tl.length; i++) {
if (tl[i].hours) {
total_working_hr += tl[i].hours;
- total_billing_hr += tl[i].billing_hours;
- total_billing_amount += tl[i].billing_amount;
+ total_billable_amount += tl[i].billing_amount;
total_costing_amount += tl[i].costing_amount;
+
+ if(tl[i].billable){
+ total_billing_hr += tl[i].billing_hours;
+ }
}
}
- cur_frm.set_value("total_billing_hours", total_billing_hr);
+ cur_frm.set_value("total_billable_hours", total_billing_hr);
cur_frm.set_value("total_hours", total_working_hr);
- cur_frm.set_value("total_billing_amount", total_billing_amount);
+ cur_frm.set_value("total_billable_amount", total_billable_amount);
cur_frm.set_value("total_costing_amount", total_costing_amount);
}
\ No newline at end of file
diff --git a/erpnext/projects/doctype/timesheet/timesheet.json b/erpnext/projects/doctype/timesheet/timesheet.json
index cba4859..168bc63 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.json
+++ b/erpnext/projects/doctype/timesheet/timesheet.json
@@ -470,8 +470,8 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "fieldname": "working_hours",
"columns": 0,
+ "fieldname": "working_hours",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -522,6 +522,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
+ "columns": 0,
"fieldname": "billing_details",
"fieldtype": "Section Break",
"hidden": 0,
@@ -544,24 +545,77 @@
"unique": 0
},
{
- "allow_on_submit": 0,
+ "allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
- "fieldname": "total_billing_hours",
+ "columns": 0,
+ "fieldname": "total_billable_hours",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "Total Billing Hours",
+ "label": "Total Billable Hours",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
- "print_hide": 0,
+ "print_hide": 1,
"print_hide_if_no_value": 0,
- "read_only": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "total_billed_hours",
+ "fieldtype": "Float",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Total Billed Hours",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "total_costing_amount",
+ "fieldtype": "Float",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Total Costing Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -601,14 +655,14 @@
"default": "0",
"depends_on": "",
"description": "",
- "fieldname": "total_billing_amount",
+ "fieldname": "total_billable_amount",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "Total Billing Amount",
+ "label": "Total Billable Amount",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -623,23 +677,49 @@
"unique": 0
},
{
- "allow_on_submit": 0,
+ "allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "total_costing_amount",
+ "fieldname": "total_billed_amount",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "Total Costing Amount",
+ "label": "Total Billed Amount",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
- "print_hide": 0,
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "per_billed",
+ "fieldtype": "Percent",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "% Amount Billed",
+ "length": 0,
+ "no_copy": 1,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
@@ -737,7 +817,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-09-01 11:33:38.110421",
+ "modified": "2016-09-12 13:19:22.298036",
"modified_by": "Administrator",
"module": "Projects",
"name": "Timesheet",
diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py
index 31a98d9..3b8f939 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.py
+++ b/erpnext/projects/doctype/timesheet/timesheet.py
@@ -8,6 +8,7 @@
import json
from datetime import timedelta
+from erpnext.controllers.queries import get_match_cond
from frappe.utils import flt, time_diff_in_hours, get_datetime, getdate, cint, get_datetime_str
from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc
@@ -20,26 +21,41 @@
class Timesheet(Document):
def validate(self):
+ self.set_employee_name()
self.set_status()
self.validate_dates()
self.validate_time_logs()
self.update_cost()
self.calculate_total_amounts()
+ self.calculate_percentage_billed()
+
+ def set_employee_name(self):
+ if self.employee and not self.employee_name:
+ self.employee_name = frappe.db.get_value('Employee', self.employee, 'employee_name')
def calculate_total_amounts(self):
self.total_hours = 0.0
- self.total_billing_hours = 0.0
- self.total_billing_amount = 0.0
+ self.total_billable_hours = 0.0
+ self.total_billed_hours = 0.0
+ self.total_billable_amount = 0.0
self.total_costing_amount = 0.0
+ self.total_billed_amount = 0.0
for d in self.get("time_logs"):
self.update_billing_hours(d)
self.total_hours += flt(d.hours)
- self.total_billing_hours += flt(d.billing_hours)
- if d.billable:
- self.total_billing_amount += flt(d.billing_amount)
+ if d.billable:
+ self.total_billable_hours += flt(d.billing_hours)
+ self.total_billable_amount += flt(d.billing_amount)
self.total_costing_amount += flt(d.costing_amount)
+ self.total_billed_amount += flt(d.billing_amount) if d.sales_invoice else 0.0
+ self.total_billed_hours += flt(d.billing_hours) if d.sales_invoice else 0.0
+
+ def calculate_percentage_billed(self):
+ self.per_billed = 0
+ if self.total_billed_amount > 0 and self.total_billable_amount > 0:
+ self.per_billed = (self.total_billed_amount * 100) / self.total_billable_amount
def update_billing_hours(self, args):
if cint(args.billing_hours) == 0:
@@ -52,7 +68,7 @@
"2": "Cancelled"
}[str(self.docstatus or 0)]
- if self.sales_invoice:
+ if self.per_billed == 100:
self.status = "Billed"
if self.salary_slip:
@@ -236,31 +252,70 @@
def update_cost(self):
for data in self.time_logs:
- if data.activity_type and (not data.billing_amount or not data.costing_amount):
+ if data.activity_type and data.billable:
rate = get_activity_cost(self.employee, data.activity_type)
hours = data.billing_hours or 0
if rate:
- data.billing_rate = flt(rate.get('billing_rate'))
- data.costing_rate = flt(rate.get('costing_rate'))
+ data.billing_rate = flt(rate.get('billing_rate')) if flt(data.billing_rate) == 0 else data.billing_rate
+ data.costing_rate = flt(rate.get('costing_rate')) if flt(data.costing_rate) == 0 else data.costing_rate
data.billing_amount = data.billing_rate * hours
data.costing_amount = data.costing_rate * hours
@frappe.whitelist()
+def get_projectwise_timesheet_data(project, parent=None):
+ cond = ''
+ if parent:
+ cond = "and parent = %(parent)s"
+
+ return frappe.db.sql("""select name, parent, billing_hours, billing_amount as billing_amt
+ from `tabTimesheet Detail` where docstatus=1 and project = %(project)s {0} and billable = 1
+ and sales_invoice is null""".format(cond), {'project': project, 'parent': parent}, as_dict=1)
+
+@frappe.whitelist()
+def get_timesheet(doctype, txt, searchfield, start, page_len, filters):
+ if not filters: filters = {}
+
+ condition = ""
+ if filters.get("project"):
+ condition = "and tsd.project = %(project)s"
+
+ return frappe.db.sql("""select distinct tsd.parent from `tabTimesheet Detail` tsd,
+ `tabTimesheet` ts where
+ ts.status in ('Submitted', 'Payslip') and tsd.parent = ts.name and
+ tsd.docstatus = 1 and ts.total_billable_amount > 0
+ and tsd.parent LIKE %(txt)s {condition}
+ order by tsd.parent limit %(start)s, %(page_len)s"""
+ .format(condition=condition), {
+ "txt": "%%%s%%" % frappe.db.escape(txt),
+ "start": start, "page_len": page_len, 'project': filters.get("project")
+ })
+
+@frappe.whitelist()
+def get_timesheet_data(name, project):
+ if project and project!='':
+ data = get_projectwise_timesheet_data(project, name)
+ else:
+ data = frappe.get_all('Timesheet',
+ fields = ["(total_billable_amount - total_billed_amount) as billing_amt", "total_billable_hours as billing_hours"], filters = {'name': name})
+
+ return {
+ 'billing_hours': data[0].billing_hours,
+ 'billing_amount': data[0].billing_amt,
+ 'timesheet_detail': data[0].name if project and project!= '' else None
+ }
+
+@frappe.whitelist()
def make_sales_invoice(source_name, target=None):
target = frappe.new_doc("Sales Invoice")
+ timesheet = frappe.get_doc('Timesheet', source_name)
- target.append("timesheets", get_mapped_doc("Timesheet", source_name, {
- "Timesheet": {
- "doctype": "Sales Invoice Timesheet",
- "field_map": {
- "total_billing_amount": "billing_amount",
- "total_billing_hours": "billing_hours",
- "name": "time_sheet"
- },
- }
- }))
-
- target.run_method("calculate_billing_amount_from_timesheet")
+ target.append('timesheets', {
+ 'time_sheet': timesheet.name,
+ 'billing_hours': flt(timesheet.total_billable_hours) - flt(timesheet.total_billed_hours),
+ 'billing_amount': flt(timesheet.total_billable_amount) - flt(timesheet.total_billed_amount)
+ })
+
+ target.run_method("calculate_billing_amount_for_timesheet")
return target
@@ -300,7 +355,7 @@
["costing_rate", "billing_rate"], as_dict=True)
return rate[0] if rate else {}
-
+
@frappe.whitelist()
def get_events(start, end, filters=None):
"""Returns events for Gantt / Calendar view rendering.
@@ -311,10 +366,14 @@
filters = json.loads(filters)
conditions = get_conditions(filters)
- return frappe.db.sql("""select `tabTimesheet Detail`.name as name, `tabTimesheet Detail`.docstatus as status, `tabTimesheet Detail`.parent as parent,
- from_time, hours, activity_type, project, to_time from `tabTimesheet Detail`,
- `tabTimesheet` where `tabTimesheet Detail`.parent = `tabTimesheet`.name and `tabTimesheet`.docstatus < 2 and
- (from_time between %(start)s and %(end)s) {conditions}""".format(conditions=conditions),
+ return frappe.db.sql("""select `tabTimesheet Detail`.name as name,
+ `tabTimesheet Detail`.docstatus as status, `tabTimesheet Detail`.parent as parent,
+ from_time, hours, activity_type, project, to_time
+ from `tabTimesheet Detail`, `tabTimesheet`
+ where `tabTimesheet Detail`.parent = `tabTimesheet`.name
+ and `tabTimesheet`.docstatus < 2
+ and (from_time between %(start)s and %(end)s) {conditions} {match_cond}
+ """.format(conditions=conditions, match_cond = get_match_cond('Timesheet')),
{
"start": start,
"end": end
diff --git a/erpnext/projects/doctype/timesheet_detail/timesheet_detail.json b/erpnext/projects/doctype/timesheet_detail/timesheet_detail.json
index 9bd4b8d..b1d3232 100644
--- a/erpnext/projects/doctype/timesheet_detail/timesheet_detail.json
+++ b/erpnext/projects/doctype/timesheet_detail/timesheet_detail.json
@@ -11,33 +11,6 @@
"editable_grid": 1,
"fields": [
{
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "columns": 1,
- "depends_on": "",
- "fieldname": "billable",
- "fieldtype": "Check",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 1,
- "label": "Bill",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -170,192 +143,6 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "depends_on": "billable",
- "fieldname": "section_break_11",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "fieldname": "billing_hours",
- "fieldtype": "Float",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Billing Hours",
- "length": 0,
- "no_copy": 0,
- "permlevel": 1,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "",
- "fieldname": "billing_rate",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Billing Rate",
- "length": 0,
- "no_copy": 0,
- "permlevel": 1,
- "precision": "2",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "costing_rate",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Costing Rate",
- "length": 0,
- "no_copy": 0,
- "permlevel": 1,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_14",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "0",
- "depends_on": "",
- "description": "",
- "fieldname": "billing_amount",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Billing Amount",
- "length": 0,
- "no_copy": 0,
- "permlevel": 1,
- "precision": "2",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 1,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "0",
- "description": "",
- "fieldname": "costing_amount",
- "fieldtype": "Currency",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Costing Amount",
- "length": 0,
- "no_copy": 0,
- "permlevel": 1,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "section_break_7",
"fieldtype": "Section Break",
"hidden": 0,
@@ -407,51 +194,24 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "columns": 3,
- "fieldname": "project",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 1,
- "label": "Project",
- "length": 0,
- "no_copy": 0,
- "options": "Project",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
"columns": 0,
- "depends_on": "",
- "fieldname": "task",
+ "depends_on": "eval:parent.production_order",
+ "fieldname": "workstation",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "Task",
+ "label": "Workstation",
"length": 0,
"no_copy": 0,
- "options": "Task",
+ "options": "Workstation",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
- "read_only": 0,
+ "read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -489,34 +249,6 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:parent.production_order",
- "fieldname": "workstation",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Workstation",
- "length": 0,
- "no_copy": 0,
- "options": "Workstation",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "eval:parent.production_order",
"fieldname": "operation",
"fieldtype": "Link",
"hidden": 0,
@@ -565,6 +297,429 @@
"search_index": 0,
"set_only_once": 0,
"unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "project_details",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 3,
+ "fieldname": "project",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Project",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Project",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_2",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "",
+ "fieldname": "task",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Task",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Task",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_6",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 1,
+ "depends_on": "",
+ "fieldname": "billable",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Bill",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_8",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "billable",
+ "fieldname": "billing_hours",
+ "fieldtype": "Float",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Billing Hours",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 1,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "billable",
+ "fieldname": "section_break_11",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "",
+ "fieldname": "billing_rate",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Billing Rate",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 1,
+ "precision": "2",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "0",
+ "depends_on": "",
+ "description": "",
+ "fieldname": "billing_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Billing Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 1,
+ "precision": "2",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_14",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "costing_rate",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Costing Rate",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 1,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "0",
+ "description": "",
+ "fieldname": "costing_amount",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Costing Amount",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 1,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "reference",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Reference",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "sales_invoice",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Sales Invoice",
+ "length": 0,
+ "no_copy": 1,
+ "options": "Sales Invoice",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
}
],
"hide_heading": 0,
@@ -577,7 +732,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2016-08-26 03:00:11.431794",
+ "modified": "2016-09-09 13:36:03.057513",
"modified_by": "Administrator",
"module": "Projects",
"name": "Timesheet Detail",
diff --git a/erpnext/schools/doctype/student_admission/__init__.py b/erpnext/schools/doctype/student_admission/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/schools/doctype/student_admission/__init__.py
diff --git a/erpnext/schools/doctype/student_admission/student_admission.js b/erpnext/schools/doctype/student_admission/student_admission.js
new file mode 100644
index 0000000..48f4c46
--- /dev/null
+++ b/erpnext/schools/doctype/student_admission/student_admission.js
@@ -0,0 +1,15 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Student Admission', {
+ program: function(frm) {
+ if (frm.doc.academic_year && frm.doc.program) {
+ frm.doc.route = frappe.model.scrub(frm.doc.program) + "-" + frappe.model.scrub(frm.doc.academic_year)
+ frm.refresh_field("route");
+ }
+ },
+
+ academic_year: function(frm) {
+ frm.trigger("program");
+ }
+});
diff --git a/erpnext/schools/doctype/student_admission/student_admission.json b/erpnext/schools/doctype/student_admission/student_admission.json
new file mode 100644
index 0000000..e757ae1
--- /dev/null
+++ b/erpnext/schools/doctype/student_admission/student_admission.json
@@ -0,0 +1,427 @@
+{
+ "allow_copy": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "field:route",
+ "beta": 0,
+ "creation": "2016-09-13 03:05:27.154713",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "Document",
+ "editable_grid": 1,
+ "fields": [
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "academic_year",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Academic Year",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Academic Year",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "admission_start_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Admission Start Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "admission_end_date",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Admission End Date",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "title",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Title",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "publish",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Publish on website",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "program",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Program",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Program",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "application_fee",
+ "fieldtype": "Currency",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Application Fee",
+ "length": 0,
+ "no_copy": 0,
+ "options": "currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "currency",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Currency",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Currency",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "depends_on": "",
+ "fieldname": "route",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Route",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 1,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 1
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "application_form_route",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Application Form Route",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_5",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "introduction",
+ "fieldtype": "Text Editor",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Introduction",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "eligibility",
+ "fieldtype": "Text Editor",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Eligibility",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ }
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2016-09-15 07:03:55.581346",
+ "modified_by": "Administrator",
+ "module": "Schools",
+ "name": "Student Admission",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Academics User",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 0,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "title",
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/schools/doctype/student_admission/student_admission.py b/erpnext/schools/doctype/student_admission/student_admission.py
new file mode 100644
index 0000000..c4384e2
--- /dev/null
+++ b/erpnext/schools/doctype/student_admission/student_admission.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.website.website_generator import WebsiteGenerator
+from frappe import _
+
+class StudentAdmission(WebsiteGenerator):
+ website = frappe._dict(
+ template = "templates/generators/student_admission.html",
+ condition_field = "publish",
+ page_title_field = "route"
+ )
+
+ def get_context(self, context):
+ context.parents = [{'name': 'admissions', 'title': _('All Student Admissions') }]
+
+ def validate(self):
+ if not self.title:
+ self.title = self.program + " admissions for " + self.academic_year
+
+def get_list_context(context):
+ context.title = _("Student Admissions")
diff --git a/erpnext/schools/doctype/student_admission/test_student_admission.py b/erpnext/schools/doctype/student_admission/test_student_admission.py
new file mode 100644
index 0000000..748c7ae
--- /dev/null
+++ b/erpnext/schools/doctype/student_admission/test_student_admission.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+import frappe
+import unittest
+
+# test_records = frappe.get_test_records('Student Admission')
+
+class TestStudentAdmission(unittest.TestCase):
+ pass
diff --git a/erpnext/schools/doctype/student_sibling/student_sibling.json b/erpnext/schools/doctype/student_sibling/student_sibling.json
index d620987..b74d48c 100644
--- a/erpnext/schools/doctype/student_sibling/student_sibling.json
+++ b/erpnext/schools/doctype/student_sibling/student_sibling.json
@@ -14,7 +14,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "columns": 4,
+ "columns": 3,
"fieldname": "full_name",
"fieldtype": "Data",
"hidden": 0,
@@ -41,7 +41,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "columns": 0,
+ "columns": 1,
"fieldname": "gender",
"fieldtype": "Select",
"hidden": 0,
@@ -57,6 +57,33 @@
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
+ "print_width": "",
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 2,
+ "fieldname": "date_of_birth",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Date of Birth",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -69,14 +96,65 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "date_of_birth",
- "fieldtype": "Date",
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 2,
+ "fieldname": "program",
+ "fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
- "label": "Date of Birth",
+ "label": "Program",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 2,
+ "fieldname": "institution",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Institution",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -101,7 +179,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
- "modified": "2016-09-13 12:39:05.078062",
+ "modified": "2016-09-14 06:03:44.067781",
"modified_by": "Administrator",
"module": "Schools",
"name": "Student Sibling",
diff --git a/erpnext/schools/web_form/student_applicant/__init__.py b/erpnext/schools/web_form/student_applicant/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/schools/web_form/student_applicant/__init__.py
diff --git a/erpnext/schools/web_form/student_applicant/student_applicant.js b/erpnext/schools/web_form/student_applicant/student_applicant.js
new file mode 100644
index 0000000..699703c
--- /dev/null
+++ b/erpnext/schools/web_form/student_applicant/student_applicant.js
@@ -0,0 +1,3 @@
+frappe.ready(function() {
+ // bind events here
+})
\ No newline at end of file
diff --git a/erpnext/schools/web_form/student_applicant/student_applicant.json b/erpnext/schools/web_form/student_applicant/student_applicant.json
new file mode 100644
index 0000000..63ad764
--- /dev/null
+++ b/erpnext/schools/web_form/student_applicant/student_applicant.json
@@ -0,0 +1,159 @@
+{
+ "allow_comments": 0,
+ "allow_delete": 0,
+ "allow_edit": 1,
+ "allow_multiple": 1,
+ "creation": "2016-09-12 02:26:42.447103",
+ "doc_type": "Student Applicant",
+ "docstatus": 0,
+ "doctype": "Web Form",
+ "idx": 0,
+ "is_standard": 1,
+ "login_required": 1,
+ "modified": "2016-09-15 02:00:28.493759",
+ "modified_by": "Administrator",
+ "module": "Schools",
+ "name": "student-applicant",
+ "owner": "Administrator",
+ "published": 1,
+ "route": "student-applicant",
+ "sidebar_items": [],
+ "success_url": "/student-applicant",
+ "title": "Student Applicant",
+ "web_form_fields": [
+ {
+ "fieldname": "first_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "label": "First Name",
+ "read_only": 0,
+ "reqd": 1
+ },
+ {
+ "fieldname": "middle_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "label": "Middle Name",
+ "read_only": 0,
+ "reqd": 0
+ },
+ {
+ "fieldname": "last_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "label": "Last Name",
+ "read_only": 0,
+ "reqd": 0
+ },
+ {
+ "fieldname": "image",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "label": "Image",
+ "read_only": 0,
+ "reqd": 0
+ },
+ {
+ "fieldname": "program",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "label": "Program",
+ "options": "Program",
+ "read_only": 0,
+ "reqd": 1
+ },
+ {
+ "fieldname": "academic_year",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "label": "Academic Year",
+ "options": "Academic Year",
+ "read_only": 0,
+ "reqd": 0
+ },
+ {
+ "fieldname": "date_of_birth",
+ "fieldtype": "Date",
+ "hidden": 0,
+ "label": "Date of Birth",
+ "read_only": 0,
+ "reqd": 0
+ },
+ {
+ "fieldname": "blood_group",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "label": "Blood Group",
+ "options": "\nA+\nA-\nB+\nB-\nO+\nO-\nAB+\nAB-",
+ "read_only": 0,
+ "reqd": 0
+ },
+ {
+ "fieldname": "student_email_id",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "label": "Student Email ID",
+ "read_only": 0,
+ "reqd": 0
+ },
+ {
+ "fieldname": "student_mobile_number",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "label": "Student Mobile Number",
+ "read_only": 0,
+ "reqd": 0
+ },
+ {
+ "fieldname": "nationality",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "label": "Nationality",
+ "options": "Country",
+ "read_only": 0,
+ "reqd": 0
+ },
+ {
+ "fieldname": "address_line_1",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "label": "Address Line 1",
+ "read_only": 0,
+ "reqd": 0
+ },
+ {
+ "fieldname": "address_line_2",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "label": "Address Line 2",
+ "read_only": 0,
+ "reqd": 0
+ },
+ {
+ "fieldname": "pincode",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "label": "Pincode",
+ "read_only": 0,
+ "reqd": 0
+ },
+ {
+ "fieldname": "guardians",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "label": "Guardians",
+ "options": "Student Guardian",
+ "read_only": 0,
+ "reqd": 0
+ },
+ {
+ "fieldname": "siblings",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "label": "Siblings",
+ "options": "Student Sibling",
+ "read_only": 0,
+ "reqd": 0
+ }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/schools/web_form/student_applicant/student_applicant.py b/erpnext/schools/web_form/student_applicant/student_applicant.py
new file mode 100644
index 0000000..2334f8b
--- /dev/null
+++ b/erpnext/schools/web_form/student_applicant/student_applicant.py
@@ -0,0 +1,7 @@
+from __future__ import unicode_literals
+
+import frappe
+
+def get_context(context):
+ # do your magic here
+ pass
diff --git a/erpnext/setup/doctype/item_group/item_group.js b/erpnext/setup/doctype/item_group/item_group.js
index 46d4034..c55c7cb 100644
--- a/erpnext/setup/doctype/item_group/item_group.js
+++ b/erpnext/setup/doctype/item_group/item_group.js
@@ -20,7 +20,13 @@
frm.trigger("set_root_readonly");
frm.add_custom_button(__("Item Group Tree"), function() {
frappe.set_route("Tree", "Item Group");
- }, "icon-sitemap");
+ });
+
+ if(!frm.is_new()) {
+ frm.add_custom_button(__("Items"), function() {
+ frappe.set_route("List", "Item", {"item_group": frm.doc.name});
+ });
+ }
},
set_root_readonly: function(frm) {
diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py
index 5375db0..12899cd 100644
--- a/erpnext/setup/doctype/item_group/item_group.py
+++ b/erpnext/setup/doctype/item_group/item_group.py
@@ -60,13 +60,15 @@
def get_context(self, context):
context.show_search=True
+ context.page_length = 6
context.search_link = '/product_search'
start = int(frappe.form_dict.start or 0)
if start < 0:
start = 0
context.update({
- "items": get_product_list_for_group(product_group = self.name, start=start, limit=24, search=frappe.form_dict.get("search")),
+ "items": get_product_list_for_group(product_group = self.name, start=start,
+ limit=context.page_length, search=frappe.form_dict.get("search")),
"parent_groups": get_parent_item_groups(self.name),
"title": self.name,
"products_as_list": cint(frappe.db.get_single_value('Website Settings', 'products_as_list'))
diff --git a/erpnext/setup/setup_wizard/install_fixtures.py b/erpnext/setup/setup_wizard/install_fixtures.py
index 23549ff..84ac157 100644
--- a/erpnext/setup/setup_wizard/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/install_fixtures.py
@@ -30,7 +30,7 @@
# salary component
{'doctype': 'Salary Component', 'salary_component': _('Income Tax'), 'description': _('Income Tax')},
{'doctype': 'Salary Component', 'salary_component': _('Basic'), 'description': _('Basic')},
-
+
# expense claim type
{'doctype': 'Expense Claim Type', 'name': _('Calls'), 'expense_type': _('Calls')},
{'doctype': 'Expense Claim Type', 'name': _('Food'), 'expense_type': _('Food')},
@@ -209,5 +209,4 @@
# make sure DuplicateEntryError is for the exact same doc and not a related doc
pass
else:
- raise
-
+ raise
\ No newline at end of file
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index b546df4..454fc70 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -455,16 +455,18 @@
def cant_change(self):
if not self.get("__islocal"):
- vals = frappe.db.get_value("Item", self.name, ["has_serial_no", "is_stock_item",
- "valuation_method", "has_batch_no", "is_fixed_asset"], as_dict=True)
+ to_check = ("has_serial_no", "is_stock_item",
+ "valuation_method", "has_batch_no", "is_fixed_asset")
- if vals and ((self.is_stock_item != vals.is_stock_item) or
- vals.has_serial_no != self.has_serial_no or
- vals.has_batch_no != self.has_batch_no or
- cstr(vals.valuation_method) != cstr(self.valuation_method)):
- if self.check_if_linked_document_exists():
- frappe.throw(_("As there are existing transactions for this item, \
- you can not change the values of 'Has Serial No', 'Has Batch No', 'Is Stock Item' and 'Valuation Method'"))
+ vals = frappe.db.get_value("Item", self.name, to_check, as_dict=True)
+
+ if vals:
+ for key in to_check:
+ if self.get(key) != vals.get(key):
+ if not self.check_if_linked_document_exists():
+ break # no linked document, allowed
+ else:
+ frappe.throw(_("As there are existing transactions for this item, you can not change the value of {0}").format(frappe.bold(self.meta.get_label(key))))
if vals and not self.is_fixed_asset and self.is_fixed_asset != vals.is_fixed_asset:
asset = frappe.db.get_all("Asset", filters={"item_code": self.name, "docstatus": 1}, limit=1)
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 7f0b25b..7c9e1e6 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -40,14 +40,6 @@
}
}
}
- this.frm.set_query("difference_account", function() {
- return {
- "filters": {
- "company": me.frm.doc.company,
- "is_group": 0
- }
- };
- });
}
},
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json
index b5efc90..0ab80c2 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.json
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.json
@@ -847,33 +847,6 @@
{
"allow_on_submit": 0,
"bold": 0,
- "collapsible": 0,
- "description": "",
- "fieldname": "difference_account",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Difference Account",
- "length": 0,
- "no_copy": 0,
- "options": "Account",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
- {
- "allow_on_submit": 0,
- "bold": 0,
"collapsible": 1,
"collapsible_depends_on": "total_additional_costs",
"fieldname": "additional_costs_section",
@@ -1431,7 +1404,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-04-07 06:40:03.284036",
+ "modified": "2016-09-08 06:40:03.284036",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Entry",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index c2d1abe..44782d5 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -71,9 +71,6 @@
if self.purpose not in valid_purposes:
frappe.throw(_("Purpose must be one of {0}").format(comma_or(valid_purposes)))
- if self.purpose in ("Manufacture", "Repack") and not self.difference_account:
- self.difference_account = frappe.db.get_value("Company", self.company, "default_expense_account")
-
def set_transfer_qty(self):
for item in self.get("items"):
if not flt(item.qty):
@@ -97,9 +94,6 @@
if f in ["stock_uom", "conversion_factor"] or not item.get(f):
item.set(f, item_details.get(f))
- if self.difference_account and not item.expense_account:
- item.expense_account = self.difference_account
-
if not item.transfer_qty and item.qty:
item.transfer_qty = item.qty * item.conversion_factor
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
index b75eeea..cb3bb7b 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
@@ -57,8 +57,6 @@
s.purchase_receipt_no = args.purchase_receipt_no
s.delivery_note_no = args.delivery_note_no
s.sales_invoice_no = args.sales_invoice_no
- if args.difference_account:
- s.difference_account = args.difference_account
if not args.cost_center:
args.cost_center = frappe.get_value('Company', s.company, 'cost_center')
diff --git a/erpnext/templates/generators/item_group.html b/erpnext/templates/generators/item_group.html
index b9926d6..674143d 100644
--- a/erpnext/templates/generators/item_group.html
+++ b/erpnext/templates/generators/item_group.html
@@ -31,10 +31,10 @@
</div>
<div class="text-center item-group-nav-buttons">
{% if frappe.form_dict.start|int > 0 %}
- <a class="btn btn-default" href="{{ pathname }}?start={{ frappe.form_dict.start|int - 24 }}">Prev</a>
+ <a class="btn btn-default" href="/{{ pathname }}?start={{ frappe.form_dict.start|int - page_length }}">Prev</a>
{% endif %}
- {% if items|length == 24 %}
- <a class="btn btn-default" href="{{ pathname }}?start={{ frappe.form_dict.start|int + 24 }}">Next</a>
+ {% if items|length == page_length %}
+ <a class="btn btn-default" href="/{{ pathname }}?start={{ frappe.form_dict.start|int + page_length }}">Next</a>
{% endif %}
</div>
{% else %}
diff --git a/erpnext/templates/generators/student_admission.html b/erpnext/templates/generators/student_admission.html
new file mode 100644
index 0000000..7eb9226
--- /dev/null
+++ b/erpnext/templates/generators/student_admission.html
@@ -0,0 +1,55 @@
+
+{% extends "templates/web.html" %}
+
+{% block breadcrumbs %}
+ {% include "templates/includes/breadcrumbs.html" %}
+{% endblock %}
+
+{% block header %}
+
+<h1>{{ title }}</h1>
+{% endblock %}
+
+{% block page_content %}
+
+{%- if introduction -%}
+<div>{{ introduction }}</div>
+{% endif %}
+
+{%- if eligibility -%}
+<h3> Eligibility </h3>
+<div>{{ eligibility }}</div>
+{% endif %}
+
+<table class="table table-bordered" style="margin-top: 20px; width: 30%">
+ <tr>
+ <th> Program</th>
+ <td>{{ program }} </td>
+ </tr>
+ <tr>
+ <th> Academic Year</th>
+ <td>{{ academic_year }} </td>
+ </tr>
+ <tr>
+ <th> Admission Start Date</th>
+ <td>{{ frappe.utils.formatdate(admission_start_date) }}</td>
+ </tr>
+ <tr>
+ <th> Admission End Date</th>
+ <td>{{ frappe.utils.formatdate(admission_end_date) }}</td>
+ </tr>
+ <tr>
+ <th> Application Fee</th>
+ <td>{{ frappe.utils.fmt_money(application_fee, 2, currency) }}</td>
+ </tr>
+</table>
+
+{%- if application_form_route -%}
+<p>
+ <a class='btn btn-primary'
+ href='/{{ doc.application_form_route }}?program={{ doc.program }}&academic_year={{ doc.academic_year }}&new=1'>
+ {{ _("Apply Now") }}</a>
+</p>
+{% endif %}
+
+{% endblock %}
diff --git a/erpnext/utilities/doctype/address_template/address_template.js b/erpnext/utilities/doctype/address_template/address_template.js
index 83d4fc8..c055bca 100644
--- a/erpnext/utilities/doctype/address_template/address_template.js
+++ b/erpnext/utilities/doctype/address_template/address_template.js
@@ -3,6 +3,14 @@
frappe.ui.form.on('Address Template', {
refresh: function(frm) {
-
+ if(frm.is_new() && !frm.doc.template) {
+ // set default template via js so that it is translated
+ frappe.call({
+ method: 'erpnext.utilities.doctype.address_template.address_template.get_default_address_template',
+ callback: function(r) {
+ frm.set_value('template', r.message);
+ }
+ });
+ }
}
});
diff --git a/erpnext/utilities/doctype/address_template/address_template.json b/erpnext/utilities/doctype/address_template/address_template.json
index 5002619..3e263be 100644
--- a/erpnext/utilities/doctype/address_template/address_template.json
+++ b/erpnext/utilities/doctype/address_template/address_template.json
@@ -15,6 +15,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "country",
"fieldtype": "Link",
"hidden": 0,
@@ -40,6 +41,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"description": "This format is used if country specific format is not found",
"fieldname": "is_default",
"fieldtype": "Check",
@@ -65,7 +67,8 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
- "default": "{{ address_line1 }}<br>{% if address_line2 %}{{ address_line2 }}<br>{% endif -%}\n{{ city }}<br>\n{% if state %}{{ state }}<br>{% endif -%}\n{% if pincode %}{{ pincode }}<br>{% endif -%}\n{{ country }}<br>\n{% if phone %}Phone: {{ phone }}<br>{% endif -%}\n{% if fax %}Fax: {{ fax }}<br>{% endif -%}\n{% if email_id %}Email: {{ email_id }}<br>{% endif -%}\n",
+ "columns": 0,
+ "default": "",
"description": "<h4>Default Template</h4>\n<p>Uses <a href=\"http://jinja.pocoo.org/docs/templates/\">Jinja Templating</a> and all the fields of Address (including Custom Fields if any) will be available</p>\n<pre><code>{{ address_line1 }}<br>\n{% if address_line2 %}{{ address_line2 }}<br>{% endif -%}\n{{ city }}<br>\n{% if state %}{{ state }}<br>{% endif -%}\n{% if pincode %} PIN: {{ pincode }}<br>{% endif -%}\n{{ country }}<br>\n{% if phone %}Phone: {{ phone }}<br>{% endif -%}\n{% if fax %}Fax: {{ fax }}<br>{% endif -%}\n{% if email_id %}Email: {{ email_id }}<br>{% endif -%}\n</code></pre>",
"fieldname": "template",
"fieldtype": "Code",
@@ -99,7 +102,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-07-25 05:24:26.636240",
+ "modified": "2016-09-15 05:42:59.542484",
"modified_by": "Administrator",
"module": "Utilities",
"name": "Address Template",
diff --git a/erpnext/utilities/doctype/address_template/address_template.py b/erpnext/utilities/doctype/address_template/address_template.py
index e759be4..64aaa45 100644
--- a/erpnext/utilities/doctype/address_template/address_template.py
+++ b/erpnext/utilities/doctype/address_template/address_template.py
@@ -9,6 +9,9 @@
class AddressTemplate(Document):
def validate(self):
+ if not self.template:
+ self.template = get_default_address_template()
+
self.defaults = frappe.db.get_values("Address Template", {"is_default":1, "name":("!=", self.name)})
if not self.is_default:
if not self.defaults:
@@ -25,3 +28,15 @@
def on_trash(self):
if self.is_default:
frappe.throw(_("Default Address Template cannot be deleted"))
+
+@frappe.whitelist()
+def get_default_address_template():
+ '''Get default address template (translated)'''
+ return '''{{ address_line1 }}<br>{% if address_line2 %}{{ address_line2 }}<br>{% endif -%}\
+{{ city }}<br>
+{% if state %}{{ state }}<br>{% endif -%}
+{% if pincode %}{{ pincode }}<br>{% endif -%}
+{{ country }}<br>
+{% if phone %}'''+_('Phone')+''': {{ phone }}<br>{% endif -%}
+{% if fax %}'''+_('Fax')+''': {{ fax }}<br>{% endif -%}
+{% if email_id %}'''+_('Email')+''': {{ email_id }}<br>{% endif -%}'''