Merge branch 'develop'

This commit is contained in:
Nabin Hait 2015-11-16 16:42:56 +05:30
commit a975fea3ac
38 changed files with 1043 additions and 920 deletions

View File

@ -1,2 +1,2 @@
from __future__ import unicode_literals
__version__ = '6.8.4'
__version__ = '6.9.0'

View File

@ -21,11 +21,13 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
if(doc.docstatus == 1 && !in_list(["Stopped", "Closed", "Delivered"], doc.status)) {
if(flt(doc.per_billed, 2) < 100 || doc.per_received < 100) {
cur_frm.add_custom_button(__('Stop'), this.stop_purchase_order);
}
if (this.frm.has_perm("submit")) {
if(flt(doc.per_billed, 2) < 100 || doc.per_received < 100) {
cur_frm.add_custom_button(__('Stop'), this.stop_purchase_order);
}
cur_frm.add_custom_button(__('Close'), this.close_purchase_order);
cur_frm.add_custom_button(__('Close'), this.close_purchase_order);
}
if(doc.delivered_by_supplier && doc.status!="Delivered"){
cur_frm.add_custom_button(__('Mark as Delivered'), this.delivered_by_supplier);
@ -35,7 +37,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry);
}
if(flt(doc.per_received, 2) < 100) {
if(flt(doc.per_received, 2) < 100 && this.frm.doc.__onload.has_stock_item) {
cur_frm.add_custom_button(__('Receive'), this.make_purchase_receipt).addClass("btn-primary");
if(doc.is_subcontracted==="Yes") {
@ -53,7 +55,9 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
}
if(doc.docstatus == 1 && in_list(["Stopped", "Closed", "Delivered"], doc.status)) {
cur_frm.add_custom_button(__('Re-open'), this.unstop_purchase_order);
if (this.frm.has_perm("submit")) {
cur_frm.add_custom_button(__('Re-open'), this.unstop_purchase_order);
}
}
},

View File

@ -33,6 +33,9 @@ class PurchaseOrder(BuyingController):
'overflow_type': 'order'
}]
def onload(self):
self.set_onload("has_stock_item", len(self.get_stock_items()) > 0)
def validate(self):
super(PurchaseOrder, self).validate()

View File

@ -398,17 +398,18 @@ class calculate_taxes_and_totals(object):
return
self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
total_amount_to_pay = flt(self.doc.grand_total - self.doc.total_advance - self.doc.write_off_amount,
self.doc.precision("grand_total"))
if self.doc.party_account_currency == self.doc.currency:
total_amount_to_pay = flt(self.doc.grand_total - self.doc.total_advance
- flt(self.doc.write_off_amount), self.doc.precision("grand_total"))
else:
total_amount_to_pay = flt(self.doc.base_grand_total - self.doc.total_advance
- flt(self.doc.base_write_off_amount), self.doc.precision("grand_total"))
if self.doc.doctype == "Sales Invoice":
self.doc.round_floats_in(self.doc, ["paid_amount"])
outstanding_amount = flt(total_amount_to_pay - self.doc.paid_amount, self.doc.precision("outstanding_amount"))
paid_amount = self.doc.paid_amount \
if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount),
self.doc.precision("outstanding_amount"))
elif self.doc.doctype == "Purchase Invoice":
outstanding_amount = flt(total_amount_to_pay, self.doc.precision("outstanding_amount"))
if self.doc.party_account_currency == self.doc.currency:
self.doc.outstanding_amount = outstanding_amount
else:
self.doc.outstanding_amount = flt(outstanding_amount * self.doc.conversion_rate,
self.doc.precision("outstanding_amount"))
self.doc.outstanding_amount = flt(total_amount_to_pay, self.doc.precision("outstanding_amount"))

View File

@ -29,7 +29,7 @@ blogs.
"""
app_icon = "icon-th"
app_color = "#e74c3c"
app_version = "6.8.4"
app_version = "6.9.0"
app_email = "info@erpnext.com"
app_license = "GNU General Public License (v3)"
source_link = "https://github.com/frappe/erpnext"
@ -96,7 +96,8 @@ has_website_permission = {
"Sales Order": "erpnext.controllers.website_list_for_contact.has_website_permission",
"Sales Invoice": "erpnext.controllers.website_list_for_contact.has_website_permission",
"Delivery Note": "erpnext.controllers.website_list_for_contact.has_website_permission",
"Issue": "erpnext.support.doctype.issue.issue.has_website_permission"
"Issue": "erpnext.support.doctype.issue.issue.has_website_permission",
"Address": "erpnext.utilities.doctype.address.address.has_website_permission"
}
permission_query_conditions = {

View File

@ -19,7 +19,7 @@
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Series",
"label": "Series",
"no_copy": 1,
"options": "EXP",
"permlevel": 0,
@ -44,7 +44,7 @@
"ignore_user_permissions": 0,
"in_filter": 1,
"in_list_view": 0,
"label": "Approval Status",
"label": "Approval Status",
"no_copy": 1,
"oldfieldname": "approval_status",
"oldfieldtype": "Select",
@ -536,7 +536,7 @@
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"modified": "2015-10-02 07:38:50.191920",
"modified": "2015-11-14 12:11:13.213073",
"modified_by": "Administrator",
"module": "HR",
"name": "Expense Claim",
@ -564,7 +564,7 @@
},
{
"amend": 0,
"apply_user_permissions": 0,
"apply_user_permissions": 1,
"cancel": 0,
"create": 1,
"delete": 0,
@ -580,6 +580,7 @@
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"user_permission_doctypes": "[\"Employee\"]",
"write": 1
},
{

View File

@ -1,48 +1,62 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
cur_frm.cscript.onload = function(doc, dt, dn) {
if(!doc.posting_date) set_multiple(dt,dn,{posting_date:get_today()});
}
cur_frm.add_fetch('employee','employee_name','employee_name');
cur_frm.cscript.employee = function(doc, dt, dn) {
calculate_total_leaves_allocated(doc, dt, dn);
}
cur_frm.cscript.leave_type = function(doc, dt, dn) {
calculate_total_leaves_allocated(doc, dt, dn);
}
cur_frm.cscript.fiscal_year = function(doc, dt, dn) {
calculate_total_leaves_allocated(doc, dt, dn);
}
cur_frm.cscript.carry_forward = function(doc, dt, dn) {
calculate_total_leaves_allocated(doc, dt, dn);
}
cur_frm.cscript.carry_forwarded_leaves = function(doc, dt, dn) {
set_multiple(dt,dn,{total_leaves_allocated : flt(doc.carry_forwarded_leaves)+flt(doc.new_leaves_allocated)});
}
cur_frm.cscript.new_leaves_allocated = function(doc, dt, dn) {
set_multiple(dt,dn,{total_leaves_allocated : flt(doc.carry_forwarded_leaves)+flt(doc.new_leaves_allocated)});
}
calculate_total_leaves_allocated = function(doc, dt, dn) {
if(cint(doc.carry_forward) == 1 && doc.leave_type && doc.fiscal_year && doc.employee){
return get_server_fields('get_carry_forwarded_leaves','','', doc, dt, dn, 1);
frappe.ui.form.on("Leave Allocation", {
onload: function(frm) {
if(!frm.doc.from_date) frm.set_value("from_date", get_today());
frm.set_query("employee", function() {
return {
query: "erpnext.controllers.queries.employee_query"
}
})
},
employee: function(frm) {
frm.trigger("calculate_total_leaves_allocated");
},
leave_type: function(frm) {
frm.trigger("calculate_total_leaves_allocated");
},
carry_forward: function(frm) {
frm.trigger("calculate_total_leaves_allocated");
},
carry_forwarded_leaves: function(frm) {
frm.set_value("total_leaves_allocated",
flt(frm.doc.carry_forwarded_leaves) + flt(frm.doc.new_leaves_allocated));
},
new_leaves_allocated: function(frm) {
frm.set_value("total_leaves_allocated",
flt(frm.doc.carry_forwarded_leaves) + flt(frm.doc.new_leaves_allocated));
},
calculate_total_leaves_allocated: function(frm) {
if (cint(frm.doc.carry_forward) == 1 && frm.doc.leave_type && frm.doc.employee) {
return frappe.call({
method: "erpnext.hr.doctype.leave_allocation.leave_allocation.get_carry_forwarded_leaves",
args: {
"employee": frm.doc.employee,
"date": frm.doc.from_date,
"leave_type": frm.doc.leave_type,
"carry_forward": frm.doc.carry_forward
},
callback: function(r) {
if (!r.exc && r.message) {
frm.set_value('carry_forwarded_leaves', r.message);
frm.set_value("total_leaves_allocated",
flt(r.message) + flt(frm.doc.new_leaves_allocated));
}
}
})
} else if (cint(frm.doc.carry_forward) == 0) {
frm.set_value("carry_forwarded_leaves", 0);
frm.set_value("total_leaves_allocated", flt(frm.doc.new_leaves_allocated));
}
}
else if(cint(doc.carry_forward) == 0){
set_multiple(dt,dn,{carry_forwarded_leaves : 0,total_leaves_allocated : flt(doc.new_leaves_allocated)});
}
}
cur_frm.fields_dict.employee.get_query = function(doc,cdt,cdn) {
return{
query: "erpnext.controllers.queries.employee_query"
}
}
})

View File

@ -3,93 +3,112 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import cint, flt, date_diff
from frappe.utils import flt, date_diff, formatdate
from frappe import _
from frappe.model.document import Document
from erpnext.hr.utils import set_employee_name
from erpnext.hr.doctype.leave_application.leave_application import get_approved_leaves_for_period
class OverlapError(frappe.ValidationError): pass
class BackDatedAllocationError(frappe.ValidationError): pass
class OverAllocationError(frappe.ValidationError): pass
class LessAllocationError(frappe.ValidationError): pass
class ValueMultiplierError(frappe.ValidationError): pass
class LeaveAllocation(Document):
def validate(self):
self.validate_period()
self.validate_new_leaves_allocated_value()
self.check_existing_leave_allocation()
if not self.total_leaves_allocated:
self.total_leaves_allocated = self.new_leaves_allocated
self.validate_allocation_overlap()
self.validate_back_dated_allocation()
self.set_total_leaves_allocated()
self.validate_total_leaves_allocated()
set_employee_name(self)
def on_update_after_submit(self):
self.validate_new_leaves_allocated_value()
def on_update(self):
self.get_total_allocated_leaves()
self.set_total_leaves_allocated()
frappe.db.set(self,'carry_forwarded_leaves', flt(self.carry_forwarded_leaves))
frappe.db.set(self,'total_leaves_allocated',flt(self.total_leaves_allocated))
self.validate_against_leave_applications()
def validate_period(self):
if date_diff(self.to_date, self.from_date) <= 0:
frappe.throw(_("Invalid period"))
frappe.throw(_("To date cannot be before from date"))
def validate_new_leaves_allocated_value(self):
"""validate that leave allocation is in multiples of 0.5"""
if flt(self.new_leaves_allocated) % 0.5:
frappe.throw(_("Leaves must be allocated in multiples of 0.5"))
frappe.throw(_("Leaves must be allocated in multiples of 0.5"), ValueMultiplierError)
def check_existing_leave_allocation(self):
"""check whether leave for same type is already allocated or not"""
leave_allocation = frappe.db.sql("""select name from `tabLeave Allocation`
where employee='%s' and leave_type='%s' and to_date >= '%s' and from_date <= '%s' and docstatus=1
"""%(self.employee, self.leave_type, self.from_date, self.to_date))
def validate_allocation_overlap(self):
leave_allocation = frappe.db.sql("""
select name from `tabLeave Allocation`
where employee=%s and leave_type=%s and docstatus=1
and to_date >= %s and from_date <= %s""",
(self.employee, self.leave_type, self.from_date, self.to_date))
if leave_allocation:
frappe.msgprint(_("Leaves for type {0} already allocated for Employee {1} for period {2} - {3}").format(self.leave_type,
self.employee, self.from_date, self.to_date))
frappe.throw(_('Reference') + ': <a href="#Form/Leave Allocation/{0}">{0}</a>'.format(leave_allocation[0][0]))
frappe.msgprint(_("{0} already allocated for Employee {1} for period {2} - {3}")
.format(self.leave_type, self.employee, formatdate(self.from_date), formatdate(self.to_date)))
frappe.throw(_('Reference') + ': <a href="#Form/Leave Allocation/{0}">{0}</a>'
.format(leave_allocation[0][0]), OverlapError)
def validate_back_dated_allocation(self):
future_allocation = frappe.db.sql("""select name, from_date from `tabLeave Allocation`
where employee=%s and leave_type=%s and docstatus=1 and from_date > %s
and carry_forward=1""", (self.employee, self.leave_type, self.to_date), as_dict=1)
if future_allocation:
frappe.throw(_("Leave cannot be allocated before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}")
.format(formatdate(future_allocation[0].from_date), future_allocation[0].name),
BackDatedAllocationError)
def get_leave_bal(self):
return self.get_leaves_allocated() - self.get_leaves_applied()
def set_total_leaves_allocated(self):
self.carry_forwarded_leaves = get_carry_forwarded_leaves(self.employee,
self.leave_type, self.from_date, self.carry_forward)
self.total_leaves_allocated = flt(self.carry_forwarded_leaves) + flt(self.new_leaves_allocated)
if not self.total_leaves_allocated:
frappe.throw(_("Total leaves allocated is mandatory"))
def get_leaves_applied(self):
leaves_applied = frappe.db.sql("""select SUM(ifnull(total_leave_days, 0))
from `tabLeave Application` where employee=%s and leave_type=%s
and to_date<=%s and docstatus=1""",
(self.employee, self.leave_type, self.from_date))
return leaves_applied and flt(leaves_applied[0][0]) or 0
def validate_total_leaves_allocated(self):
if date_diff(self.to_date, self.from_date) <= flt(self.total_leaves_allocated):
frappe.throw(_("Total allocated leaves are more than days in the period"), OverAllocationError)
def validate_against_leave_applications(self):
leaves_taken = get_approved_leaves_for_period(self.employee, self.leave_type,
self.from_date, self.to_date)
if flt(leaves_taken) > flt(self.total_leaves_allocated):
frappe.throw(_("Total allocated leaves {0} cannot be less than already approved leaves {1} for the period").format(self.total_leaves_allocated, leaves_taken), LessAllocationError)
def get_leaves_allocated(self):
leaves_allocated = frappe.db.sql("""select SUM(ifnull(total_leaves_allocated, 0))
from `tabLeave Allocation` where employee=%s and leave_type=%s
and to_date<=%s and docstatus=1 and name!=%s""",
(self.employee, self.leave_type, self.from_date, self.name))
return leaves_allocated and flt(leaves_allocated[0][0]) or 0
def allow_carry_forward(self):
"""check whether carry forward is allowed or not for this leave type"""
cf = frappe.db.sql("""select is_carry_forward from `tabLeave Type` where name = %s""",
self.leave_type)
cf = cf and cint(cf[0][0]) or 0
if not cf:
frappe.db.set(self,'carry_forward',0)
frappe.throw(_("Cannot carry forward {0}").format(self.leave_type))
def get_carry_forwarded_leaves(self):
if self.carry_forward:
self.allow_carry_forward()
prev_bal = 0
if cint(self.carry_forward) == 1:
prev_bal = self.get_leave_bal()
ret = {
'carry_forwarded_leaves': prev_bal,
'total_leaves_allocated': flt(prev_bal) + flt(self.new_leaves_allocated)
}
return ret
def get_total_allocated_leaves(self):
leave_det = self.get_carry_forwarded_leaves()
self.validate_total_leaves_allocated(leave_det)
frappe.db.set(self,'carry_forwarded_leaves',flt(leave_det['carry_forwarded_leaves']))
frappe.db.set(self,'total_leaves_allocated',flt(leave_det['total_leaves_allocated']))
def validate_total_leaves_allocated(self, leave_det):
if date_diff(self.to_date, self.from_date) <= leave_det['total_leaves_allocated']:
frappe.throw(_("Total allocated leaves are more than period"))
@frappe.whitelist()
def get_carry_forwarded_leaves(employee, leave_type, date, carry_forward=None):
carry_forwarded_leaves = 0
if carry_forward:
validate_carry_forward(leave_type)
previous_allocation = frappe.db.sql("""
select name, from_date, to_date, total_leaves_allocated
from `tabLeave Allocation`
where employee=%s and leave_type=%s and docstatus=1 and to_date < %s
order by to_date desc limit 1
""", (employee, leave_type, date), as_dict=1)
if previous_allocation:
leaves_taken = get_approved_leaves_for_period(employee, leave_type,
previous_allocation[0].from_date, previous_allocation[0].to_date)
carry_forwarded_leaves = flt(previous_allocation[0].total_leaves_allocated) - flt(leaves_taken)
return carry_forwarded_leaves
def validate_carry_forward(leave_type):
if not frappe.db.get_value("Leave Type", leave_type, "is_carry_forward"):
frappe.throw(_("Leave Type {0} cannot be carry-forwarded").format(leave_type))

View File

@ -13,7 +13,7 @@ class TestLeaveAllocation(unittest.TestCase):
"employee": employee.name,
"employee_name": employee.employee_name,
"leave_type": "_Test Leave Type",
"from_date": getdate("2015-10-1"),
"from_date": getdate("2015-10-01"),
"to_date": getdate("2015-10-31"),
"new_leaves_allocated": 5,
"docstatus": 1
@ -24,7 +24,7 @@ class TestLeaveAllocation(unittest.TestCase):
"employee": employee.name,
"employee_name": employee.employee_name,
"leave_type": "_Test Leave Type",
"from_date": getdate("2015-09-1"),
"from_date": getdate("2015-09-01"),
"to_date": getdate("2015-11-30"),
"new_leaves_allocated": 5
}

View File

@ -66,14 +66,18 @@ frappe.ui.form.on("Leave Application", {
},
get_leave_balance: function(frm) {
if(frm.doc.docstatus==0 && frm.doc.employee && frm.doc.leave_type && frm.doc.from_date && frm.doc.to_date) {
return frm.call({
method: "get_leave_balance",
if(frm.doc.docstatus==0 && frm.doc.employee && frm.doc.leave_type && frm.doc.from_date) {
return frappe.call({
method: "erpnext.hr.doctype.leave_application.leave_application.get_leave_balance_on",
args: {
employee: frm.doc.employee,
from_date: frm.doc.from_date,
to_date: frm.doc.to_date,
date: frm.doc.from_date,
leave_type: frm.doc.leave_type
},
callback: function(r) {
if (!r.exc && r.message) {
frm.set_value('leave_balance', r.message);
}
}
});
}
@ -83,14 +87,20 @@ frappe.ui.form.on("Leave Application", {
if(frm.doc.from_date && frm.doc.to_date) {
if (cint(frm.doc.half_day)==1) {
frm.set_value("total_leave_days", 0.5);
} else {
} else if (frm.doc.employee && frm.doc.leave_type){
// server call is done to include holidays in leave days calculations
return frappe.call({
method: 'erpnext.hr.doctype.leave_application.leave_application.get_total_leave_days',
args: { leave_app: frm.doc },
callback: function(response) {
if (response && response.message) {
frm.set_value('total_leave_days', response.message.total_leave_days);
method: 'erpnext.hr.doctype.leave_application.leave_application.get_number_of_leave_days',
args: {
"employee": frm.doc.employee,
"leave_type": frm.doc.leave_type,
"from_date": frm.doc.from_date,
"to_date": frm.doc.to_date,
"half_day": frm.doc.half_day
},
callback: function(r) {
if (r && r.message) {
frm.set_value('total_leave_days', r.message);
frm.trigger("get_leave_balance");
}
}

View File

@ -418,28 +418,6 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "fiscal_year",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 1,
"in_list_view": 0,
"label": "Fiscal Year",
"no_copy": 0,
"options": "Fiscal Year",
"permlevel": 0,
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 1,
"bold": 0,
@ -559,7 +537,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 3,
"modified": "2015-10-28 16:14:25.640730",
"modified": "2015-11-15 19:32:32.258397",
"modified_by": "Administrator",
"module": "HR",
"name": "Leave Application",
@ -711,7 +689,7 @@
],
"read_only": 0,
"read_only_onload": 0,
"search_fields": "employee,employee_name,leave_type,from_date,to_date,total_leave_days,fiscal_year",
"search_fields": "employee,employee_name,leave_type,from_date,to_date,total_leave_days",
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "employee_name"

View File

@ -2,13 +2,13 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe, json
import frappe
from frappe import _
from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, get_link_to_form, \
comma_or, get_fullname
from frappe import msgprint
from erpnext.hr.utils import set_employee_name
from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates
class LeaveDayBlockedError(frappe.ValidationError): pass
class OverlapError(frappe.ValidationError): pass
@ -18,8 +18,7 @@ class LeaveApproverIdentityError(frappe.ValidationError): pass
from frappe.model.document import Document
class LeaveApplication(Document):
def get_feed(self):
return _("{0}: From {0} of type {1}").format(self.status,
self.employee_name, self.leave_type)
return _("{0}: From {0} of type {1}").format(self.status, self.employee_name, self.leave_type)
def validate(self):
if not getattr(self, "__islocal", None) and frappe.db.exists(self.doctype, self.name):
@ -29,7 +28,7 @@ class LeaveApplication(Document):
set_employee_name(self)
self.validate_to_date()
self.validate_dates()
self.validate_balance_leaves()
self.validate_leave_overlap()
self.validate_max_days()
@ -50,6 +49,8 @@ class LeaveApplication(Document):
def on_submit(self):
if self.status != "Approved":
frappe.throw(_("Only Leave Applications with status 'Approved' can be submitted"))
self.validate_back_dated_application()
# notify leave applier about approval
self.notify_employee(self.status)
@ -57,10 +58,41 @@ class LeaveApplication(Document):
def on_cancel(self):
# notify leave applier about cancellation
self.notify_employee("cancelled")
def validate_dates(self):
if self.from_date and self.to_date and (getdate(self.to_date) < getdate(self.from_date)):
frappe.throw(_("To date cannot be before from date"))
self.validate_dates_acorss_allocation()
self.validate_back_dated_application()
def validate_dates_acorss_allocation(self):
def _get_leave_alloction_record(date):
allocation = frappe.db.sql("""select name from `tabLeave Allocation`
where employee=%s and leave_type=%s and docstatus=1
and %s between from_date and to_date""", (self.employee, self.leave_type, date))
return allocation and allocation[0][0]
allocation_based_on_from_date = _get_leave_alloction_record(self.from_date)
allocation_based_on_to_date = _get_leave_alloction_record(self.to_date)
if not (allocation_based_on_from_date or allocation_based_on_to_date):
frappe.throw(_("Application period cannot be outside leave allocation period"))
elif allocation_based_on_from_date != allocation_based_on_to_date:
frappe.throw(_("Application period cannot be across two alocation records"))
def validate_back_dated_application(self):
future_allocation = frappe.db.sql("""select name, from_date from `tabLeave Allocation`
where employee=%s and leave_type=%s and docstatus=1 and from_date > %s
and carry_forward=1""", (self.employee, self.leave_type, self.to_date), as_dict=1)
if future_allocation:
frappe.throw(_("Leave cannot be applied/cancelled before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}")
.format(formatdate(future_allocation[0].from_date), future_allocation[0].name))
def show_block_day_warning(self):
from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates
block_dates = get_applicable_block_dates(self.from_date, self.to_date,
self.employee, self.company, all_lists=True)
@ -70,60 +102,39 @@ class LeaveApplication(Document):
frappe.msgprint(formatdate(d.block_date) + ": " + d.reason)
def validate_block_days(self):
from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates
block_dates = get_applicable_block_dates(self.from_date, self.to_date,
self.employee, self.company)
if block_dates:
if self.status == "Approved":
frappe.throw(_("Cannot approve leave as you are not authorized to approve leaves on Block Dates"),
LeaveDayBlockedError)
def get_holidays(self):
return get_holidays(self)
def get_total_leave_days(self):
return get_total_leave_days(self)
def validate_to_date(self):
if self.from_date and self.to_date and \
(getdate(self.to_date) < getdate(self.from_date)):
frappe.throw(_("To date cannot be before from date"))
if block_dates and self.status == "Approved":
frappe.throw(_("You are not authorized to approve leaves on Block Dates"), LeaveDayBlockedError)
def validate_balance_leaves(self):
if self.from_date and self.to_date:
self.total_leave_days = self.get_total_leave_days()["total_leave_days"]
self.total_leave_days = get_number_of_leave_days(self.employee, self.leave_type,
self.from_date, self.to_date, self.half_day)
if self.total_leave_days == 0:
frappe.throw(_("The day(s) on which you are applying for leave are holiday. You need not apply for leave."))
frappe.throw(_("The day(s) on which you are applying for leave are holidays. You need not apply for leave."))
if not is_lwp(self.leave_type):
self.leave_balance = get_leave_balance(self.employee,
self.leave_type, self.from_date, self.to_date)["leave_balance"]
self.leave_balance = get_leave_balance_on(self.employee, self.leave_type, self.from_date)
if self.status != "Rejected" \
and self.leave_balance - self.total_leave_days < 0:
#check if this leave type allow the remaining balance to be in negative. If yes then warn the user and continue to save else warn the user and don't save.
if self.status != "Rejected" and self.leave_balance < self.total_leave_days:
if frappe.db.get_value("Leave Type", self.leave_type, "allow_negative"):
frappe.msgprint(_("Note: There is not enough leave balance for Leave Type {0}").format(self.leave_type))
frappe.msgprint(_("Note: There is not enough leave balance for Leave Type {0}")
.format(self.leave_type))
else:
frappe.throw(_("There is not enough leave balance for Leave Type {0}").format(self.leave_type))
frappe.throw(_("There is not enough leave balance for Leave Type {0}")
.format(self.leave_type))
def validate_leave_overlap(self):
if not self.name:
self.name = "New Leave Application"
for d in frappe.db.sql("""select name, leave_type, posting_date,
from_date, to_date
for d in frappe.db.sql("""select name, leave_type, posting_date, from_date, to_date
from `tabLeave Application`
where
employee = %(employee)s
and docstatus < 2
and status in ("Open", "Approved")
and to_date >= %(from_date)s
and from_date <= %(to_date)s
where employee = %(employee)s and docstatus < 2 and status in ("Open", "Approved")
and to_date >= %(from_date)s and from_date <= %(to_date)s
and name != %(name)s""", {
"employee": self.employee,
"from_date": self.from_date,
@ -131,9 +142,12 @@ class LeaveApplication(Document):
"name": self.name
}, as_dict = 1):
frappe.msgprint(_("Employee {0} has already applied for {1} between {2} and {3}").format(self.employee,
cstr(d['leave_type']), formatdate(d['from_date']), formatdate(d['to_date'])))
frappe.throw('<a href="#Form/Leave Application/{0}">{0}</a>'.format(d["name"]), OverlapError)
frappe.msgprint(_("Employee {0} has already applied for {1} between {2} and {3}")
.format(self.employee, cstr(d['leave_type']),
formatdate(d['from_date']), formatdate(d['to_date'])))
frappe.throw("""Exising Application: <a href="#Form/Leave Application/{0}">{0}</a>"""
.format(d["name"]), OverlapError)
def validate_max_days(self):
max_days = frappe.db.get_value("Leave Type", self.leave_type, "max_days_allowed")
@ -145,7 +159,8 @@ class LeaveApplication(Document):
leave_approvers = [l.leave_approver for l in employee.get("leave_approvers")]
if len(leave_approvers) and self.leave_approver not in leave_approvers:
frappe.throw(_("Leave approver must be one of {0}").format(comma_or(leave_approvers)), InvalidLeaveApproverError)
frappe.throw(_("Leave approver must be one of {0}")
.format(comma_or(leave_approvers)), InvalidLeaveApproverError)
elif self.leave_approver and not frappe.db.sql("""select name from `tabUserRole`
where parent=%s and role='Leave Approver'""", self.leave_approver):
@ -153,8 +168,8 @@ class LeaveApplication(Document):
.format(get_fullname(self.leave_approver), self.leave_approver), InvalidLeaveApproverError)
elif self.docstatus==1 and len(leave_approvers) and self.leave_approver != frappe.session.user:
msgprint(_("Only the selected Leave Approver can submit this Leave Application"),
raise_exception=LeaveApproverIdentityError)
frappe.throw(_("Only the selected Leave Approver can submit this Leave Application"),
LeaveApproverIdentityError)
def notify_employee(self, status):
employee = frappe.get_doc("Employee", self.employee)
@ -213,60 +228,89 @@ def get_approvers(doctype, txt, searchfield, start, page_len, filters):
approver.parent = %s
and user.name like %s
and approver.leave_approver=user.name""", (filters.get("employee"), "%" + txt + "%"))
@frappe.whitelist()
def get_number_of_leave_days(employee, leave_type, from_date, to_date, half_day=None):
if half_day:
return 0.5
number_of_days = date_diff(to_date, from_date) + 1
if not frappe.db.get_value("Leave Type", leave_type, "include_holiday"):
number_of_days = flt(number_of_days) - flt(get_holidays(employee, from_date, to_date))
return number_of_days
def get_holidays(leave_app):
@frappe.whitelist()
def get_leave_balance_on(employee, leave_type, date, allocation_records=None):
if allocation_records == None:
allocation_records = get_leave_allocation_records(date, employee).get(employee, frappe._dict())
allocation = allocation_records.get(leave_type, frappe._dict())
leaves_taken = get_approved_leaves_for_period(employee, leave_type, allocation.from_date, date)
return flt(allocation.total_leaves_allocated) - flt(leaves_taken)
def get_approved_leaves_for_period(employee, leave_type, from_date, to_date):
leave_applications = frappe.db.sql("""
select employee, leave_type, from_date, to_date, total_leave_days
from `tabLeave Application`
where employee=%(employee)s and leave_type=%(leave_type)s
and status="Approved" and docstatus=1
and (from_date between %(from_date)s and %(to_date)s
or to_date between %(from_date)s and %(to_date)s
or (from_date < %(from_date)s and to_date > %(to_date)s))
""", {
"from_date": from_date,
"to_date": to_date,
"employee": employee,
"leave_type": leave_type
}, as_dict=1)
leave_days = 0
for leave_app in leave_applications:
if leave_app.from_date >= getdate(from_date) and leave_app.to_date <= getdate(to_date):
leave_days += leave_app.total_leave_days
else:
if leave_app.from_date < getdate(from_date):
leave_app.from_date = from_date
if leave_app.to_date > getdate(to_date):
leave_app.to_date = to_date
leave_days += get_number_of_leave_days(employee, leave_type,
leave_app.from_date, leave_app.to_date)
return leave_days
def get_leave_allocation_records(date, employee=None):
conditions = (" and employee='%s'" % employee) if employee else ""
leave_allocation_records = frappe.db.sql("""
select employee, leave_type, total_leaves_allocated, from_date
from `tabLeave Allocation`
where %s between from_date and to_date and docstatus=1 {0}""".format(conditions), (date), as_dict=1)
allocated_leaves = frappe._dict()
for d in leave_allocation_records:
allocated_leaves.setdefault(d.employee, frappe._dict()).setdefault(d.leave_type, frappe._dict({
"from_date": d.from_date,
"total_leaves_allocated": d.total_leaves_allocated
}))
return allocated_leaves
def get_holidays(employee, from_date, to_date):
tot_hol = frappe.db.sql("""select count(*) from `tabHoliday` h1, `tabHoliday List` h2, `tabEmployee` e1
where e1.name = %s and h1.parent = h2.name and e1.holiday_list = h2.name
and h1.holiday_date between %s and %s""", (leave_app.employee, leave_app.from_date,
leave_app.to_date))[0][0]
# below line is needed. If an employee hasn't been assigned with any holiday list then above will return 0 rows.
and h1.holiday_date between %s and %s""", (employee, from_date, to_date))[0][0]
if not tot_hol:
tot_hol = frappe.db.sql("""select count(*) from `tabHoliday` h1, `tabHoliday List` h2
where h1.parent = h2.name and h1.holiday_date between %s and %s
and ifnull(h2.is_default,0) = 1 and h2.fiscal_year = %s""",
(leave_app.from_date, leave_app.to_date, leave_app.fiscal_year))[0][0]
and ifnull(h2.is_default,0) = 1""", (from_date, to_date))[0][0]
return tot_hol
@frappe.whitelist()
def get_total_leave_days(leave_app):
# Parse Leave Application if neccessary
if isinstance(leave_app, str) or isinstance(leave_app, unicode):
leave_app = frappe.get_doc(json.loads(leave_app))
"""Calculates total leave days based on input and holidays"""
ret = {'total_leave_days' : 0.5}
if not leave_app.half_day:
tot_days = date_diff(leave_app.to_date, leave_app.from_date) + 1
if frappe.db.get_value("Leave Type", leave_app.leave_type, "include_holiday"):
ret = {
'total_leave_days' : flt(tot_days)
}
else:
holidays = leave_app.get_holidays()
ret = {
'total_leave_days' : flt(tot_days)-flt(holidays)
}
return ret
@frappe.whitelist()
def get_leave_balance(employee, leave_type, from_date, to_date):
leave_all = frappe.db.sql("""select total_leaves_allocated
from `tabLeave Allocation` where employee = %s and leave_type = %s
and from_date<=%s and to_date>=%s and docstatus = 1""", (employee,
leave_type, from_date, to_date))
leave_all = leave_all and flt(leave_all[0][0]) or 0
leave_app = frappe.db.sql("""select SUM(total_leave_days)
from `tabLeave Application`
where employee = %s and leave_type = %s and to_date>=%s and from_date<=%s
and status="Approved" and docstatus = 1""", (employee, leave_type, from_date, to_date))
leave_app = leave_app and flt(leave_app[0][0]) or 0
ret = {'leave_balance': leave_all - leave_app}
return ret
def is_lwp(leave_type):
lwp = frappe.db.sql("select is_lwp from `tabLeave Type` where name = %s", leave_type)
return lwp and cint(lwp[0][0]) or 0

View File

@ -41,7 +41,7 @@ class ProcessPayroll(Document):
def get_joining_releiving_condition(self):
m = self.get_month_details(self.fiscal_year, self.month)
m = get_month_details(self.fiscal_year, self.month)
cond = """
and ifnull(t1.date_of_joining, '0000-00-00') <= '%(month_end_date)s'
and ifnull(t1.relieving_date, '2199-12-31') >= '%(month_start_date)s'
@ -54,24 +54,6 @@ class ProcessPayroll(Document):
if not self.get(f):
frappe.throw(_("Please set {0}").format(f))
def get_month_details(self, year, month):
ysd = frappe.db.get_value("Fiscal Year", year, "year_start_date")
if ysd:
from dateutil.relativedelta import relativedelta
import calendar, datetime
diff_mnt = cint(month)-cint(ysd.month)
if diff_mnt<0:
diff_mnt = 12-int(ysd.month)+cint(month)
msd = ysd + relativedelta(months=diff_mnt) # month start date
month_days = cint(calendar.monthrange(cint(msd.year) ,cint(month))[1]) # days in month
med = datetime.date(msd.year, cint(month), month_days) # month end date
return {
'year': msd.year,
'month_start_date': msd,
'month_end_date': med,
'month_days': month_days
}
def create_sal_slip(self):
"""
Creates salary slip for selected employees if already not created
@ -205,3 +187,22 @@ class ProcessPayroll(Document):
])
return journal_entry.as_dict()
def get_month_details(year, month):
ysd = frappe.db.get_value("Fiscal Year", year, "year_start_date")
if ysd:
from dateutil.relativedelta import relativedelta
import calendar, datetime
diff_mnt = cint(month)-cint(ysd.month)
if diff_mnt<0:
diff_mnt = 12-int(ysd.month)+cint(month)
msd = ysd + relativedelta(months=diff_mnt) # month start date
month_days = cint(calendar.monthrange(cint(msd.year) ,cint(month))[1]) # days in month
med = datetime.date(msd.year, cint(month), month_days) # month end date
return frappe._dict({
'year': msd.year,
'month_start_date': msd,
'month_end_date': med,
'month_days': month_days
})

View File

@ -10,6 +10,7 @@ from frappe.model.naming import make_autoname
from frappe import msgprint, _
from erpnext.setup.utils import get_company_currency
from erpnext.hr.utils import set_employee_name
from erpnext.hr.doctype.process_payroll.process_payroll import get_month_details
from erpnext.utilities.transaction_base import TransactionBase
@ -25,11 +26,17 @@ class SalarySlip(TransactionBase):
self.pull_sal_struct(struct)
def check_sal_struct(self):
m = get_month_details(self.fiscal_year, self.month)
struct = frappe.db.sql("""select name from `tabSalary Structure`
where employee=%s and is_active = 'Yes'""", self.employee)
where employee=%s and is_active = 'Yes'
and from_date <= %s and (to_date is null or to_date >= %s)""",
(self.employee, m.month_start_date, m.month_end_date))
if not struct:
msgprint(_("Please create Salary Structure for employee {0}").format(self.employee))
msgprint(_("No active Salary Structure found for employee {0} and the month")
.format(self.employee))
self.employee = None
return struct and struct[0][0] or ''
def pull_sal_struct(self, struct):
@ -49,7 +56,7 @@ class SalarySlip(TransactionBase):
if not self.month:
self.month = "%02d" % getdate(nowdate()).month
m = frappe.get_doc('Process Payroll').get_month_details(self.fiscal_year, self.month)
m = get_month_details(self.fiscal_year, self.month)
holidays = self.get_holidays_for_employee(m)
if not cint(frappe.db.get_value("HR Settings", "HR Settings",

View File

@ -10,8 +10,20 @@ from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_sli
class TestSalarySlip(unittest.TestCase):
def setUp(self):
frappe.db.sql("""delete from `tabLeave Application`""")
frappe.db.sql("""delete from `tabSalary Slip`""")
for dt in ["Leave Application", "Leave Allocation", "Salary Slip"]:
frappe.db.sql("delete from `tab%s`" % dt)
allocation = frappe.get_doc({
"doctype": "Leave Allocation",
"employee": "_T-Employee-0001",
"leave_type": "_Test Leave Type LWP",
"from_date": "2013-01-01",
"to_date": "2015-12-31",
"new_leaves_allocated": 5
})
allocation.insert()
allocation.submit()
frappe.db.set_value("Holiday List", "_Test Holiday List", "is_default", 1)

View File

@ -7,12 +7,14 @@ frappe.query_reports["Employee Leave Balance"] = {
"fieldname":"from_date",
"label": __("From Date"),
"fieldtype": "Date",
"reqd": 1,
"default": frappe.datetime.year_start()
},
{
"fieldname":"to_date",
"label": __("To Date"),
"fieldtype": "Date",
"reqd": 1,
"default": frappe.datetime.year_end()
},
{
@ -20,6 +22,7 @@ frappe.query_reports["Employee Leave Balance"] = {
"label": __("Company"),
"fieldtype": "Link",
"options": "Company",
"reqd": 1,
"default": frappe.defaults.get_user_default("company")
}
]

View File

@ -4,67 +4,54 @@
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.desk.reportview import execute as runreport
from erpnext.hr.doctype.leave_application.leave_application \
import get_leave_allocation_records, get_leave_balance_on, get_approved_leaves_for_period
def execute(filters=None):
if not filters: filters = {}
employee_filters = {
"status": "Active"
}
leave_types = frappe.db.sql_list("select name from `tabLeave Type` order by name asc")
if filters.get("company"):
filters["company"] = filters.company
employees = runreport(doctype="Employee", fields=["name", "employee_name", "department"],
filters=employee_filters, limit_page_length=None)
if not employees:
frappe.throw(_("No employee found!"))
leave_types = frappe.db.sql_list("select name from `tabLeave Type`")
employee_names = [d.name for d in employees]
allocations = frappe.db.sql("""select employee, leave_type, sum(new_leaves_allocated) as leaves_allocated
from `tabLeave Allocation`
where docstatus=1 and employee in (%s) and from_date >= '%s' and to_date <= '%s'""" %
(','.join(['%s']*len(employee_names)), filters.get("from_date"),
filters.get("to_date")), employee_names, as_dict=True)
applications = frappe.db.sql("""select employee, leave_type,
SUM(total_leave_days) as leaves
from `tabLeave Application`
where status="Approved" and docstatus = 1 and employee in (%s)
and from_date >= '%s' and to_date <= '%s'
group by employee, leave_type""" %
(','.join(['%s']*len(employee_names)), filters.get("from_date"),
filters.get("to_date")), employee_names, as_dict=True)
columns = get_columns(leave_types)
data = get_data(filters, leave_types)
return columns, data
def get_columns(leave_types):
columns = [
_("Employee") + ":Link/Employee:150", _("Employee Name") + "::200", _("Department") +"::150"
_("Employee") + ":Link/Employee:150",
_("Employee Name") + "::200",
_("Department") +"::150"
]
for leave_type in leave_types:
columns.append(_(leave_type) + " " + _("Opening") + ":Float")
columns.append(_(leave_type) + " " + _("Taken") + ":Float")
columns.append(_(leave_type) + " " + _("Balance") + ":Float")
columns.append(_(leave_type) + " " + _("Taken") + ":Float:160")
columns.append(_(leave_type) + " " + _("Balance") + ":Float:160")
return columns
def get_data(filters, leave_types):
data = {}
for d in allocations:
data.setdefault((d.employee,d.leave_type), frappe._dict()).allocation = d.leaves_allocated
allocation_records_based_on_to_date = get_leave_allocation_records(filters.to_date)
for d in applications:
data.setdefault((d.employee, d.leave_type), frappe._dict()).leaves = d.leaves
result = []
for employee in employees:
active_employees = frappe.get_all("Employee",
filters = { "status": "Active", "company": filters.company},
fields = ["name", "employee_name", "department"])
data = []
for employee in active_employees:
row = [employee.name, employee.employee_name, employee.department]
result.append(row)
for leave_type in leave_types:
tmp = data.get((employee.name, leave_type), frappe._dict())
row.append(tmp.allocation or 0)
row.append(tmp.leaves or 0)
row.append((tmp.allocation or 0) - (tmp.leaves or 0))
return columns, result
for leave_type in leave_types:
# leaves taken
leaves_taken = get_approved_leaves_for_period(employee.name, leave_type,
filters.from_date, filters.to_date)
# closing balance
closing = get_leave_balance_on(employee.name, leave_type, filters.to_date,
allocation_records_based_on_to_date.get(employee.name, frappe._dict()))
row += [leaves_taken, closing]
data.append(row)
return data

View File

@ -231,4 +231,5 @@ erpnext.patches.v6_4.fix_expense_included_in_valuation
execute:frappe.delete_doc_if_exists("Report", "Item-wise Last Purchase Rate")
erpnext.patches.v6_6.fix_website_image
erpnext.patches.v6_6.remove_fiscal_year_from_leave_allocation
execute:frappe.delete_doc_if_exists("DocType", "Stock UOM Replace Utility")
execute:frappe.delete_doc_if_exists("DocType", "Stock UOM Replace Utility")
erpnext.patches.v6_8.make_webform_standard

View File

View File

@ -0,0 +1,9 @@
import frappe
def execute():
frappe.reload_doctype("Web Form")
frappe.delete_doc("Web Form", "Issues")
frappe.delete_doc("Web Form", "Addresses")
from erpnext.setup.install import add_web_forms
add_web_forms()

View File

@ -4,11 +4,13 @@
frappe.provide("erpnext.projects");
frappe.ui.form.on("Time Log", "onload", function(frm) {
if (frm.doc.for_manufacturing) {
frappe.ui.form.trigger("Time Log", "production_order");
}
if (frm.doc.from_time && frm.doc.to_time) {
frappe.ui.form.trigger("Time Log", "to_time");
if (frm.doc.__islocal) {
if (frm.doc.for_manufacturing) {
frappe.ui.form.trigger("Time Log", "production_order");
}
if (frm.doc.from_time && frm.doc.to_time) {
frappe.ui.form.trigger("Time Log", "to_time");
}
}
});

View File

@ -500,9 +500,13 @@ erpnext.taxes_and_totals = erpnext.stock.StockController.extend({
if(this.frm.doc.is_return || this.frm.doc.docstatus > 0) return;
frappe.model.round_floats_in(this.frm.doc, ["grand_total", "total_advance", "write_off_amount"]);
var total_amount_to_pay = flt((this.frm.doc.grand_total - this.frm.doc.total_advance
- this.frm.doc.write_off_amount), precision("grand_total"));
if(this.frm.doc.party_account_currency == this.frm.doc.currency) {
var total_amount_to_pay = flt((this.frm.doc.grand_total - this.frm.doc.total_advance
- this.frm.doc.write_off_amount), precision("grand_total"));
else {
var total_amount_to_pay = flt((this.frm.doc.base_grand_total - this.frm.doc.total_advance
- this.frm.doc.base_write_off_amount), precision("base_grand_total"));
}
if(this.frm.doc.doctype == "Sales Invoice") {
frappe.model.round_floats_in(this.frm.doc, ["paid_amount"]);
@ -518,18 +522,15 @@ erpnext.taxes_and_totals = erpnext.stock.StockController.extend({
this.frm.refresh_field("paid_amount");
this.frm.refresh_field("base_paid_amount");
var outstanding_amount = flt(total_amount_to_pay - this.frm.doc.paid_amount,
var paid_amount = (this.frm.doc.party_account_currency == this.frm.doc.currency) ?
this.frm.doc.paid_amount : this.frm.doc.base_paid_amount;
var outstanding_amount = flt(total_amount_to_pay - flt(paid_amount),
precision("outstanding_amount"));
} else if(this.frm.doc.doctype == "Purchase Invoice") {
var outstanding_amount = flt(total_amount_to_pay, precision("outstanding_amount"));
}
if(this.frm.doc.party_account_currency == this.frm.doc.currency) {
this.frm.set_value("outstanding_amount", outstanding_amount);
} else {
this.frm.set_value("outstanding_amount",
flt(outstanding_amount * this.frm.doc.conversion_rate, precision("outstanding_amount")));
}
}
this.frm.set_value("outstanding_amount", outstanding_amount);
}
})

View File

@ -5,404 +5,407 @@ frappe.pages['setup-wizard'].on_page_load = function(wrapper) {
frappe.set_route("desk");
return;
}
}
};
$.extend(erpnext.wiz, {
region: {
title: __("Region"),
icon: "icon-flag",
help: __("Select your Country, Time Zone and Currency"),
fields: [
{ fieldname: "country", label: __("Country"), reqd:1,
fieldtype: "Select" },
{ fieldname: "timezone", label: __("Time Zone"), reqd:1,
fieldtype: "Select" },
{ fieldname: "currency", label: __("Currency"), reqd:1,
fieldtype: "Select" },
],
function load_erpnext_slides() {
$.extend(erpnext.wiz, {
region: {
title: __("Region"),
icon: "icon-flag",
help: __("Select your Country, Time Zone and Currency"),
fields: [
{ fieldname: "country", label: __("Country"), reqd:1,
fieldtype: "Select" },
{ fieldname: "timezone", label: __("Time Zone"), reqd:1,
fieldtype: "Select" },
{ fieldname: "currency", label: __("Currency"), reqd:1,
fieldtype: "Select" },
],
onload: function(slide) {
frappe.call({
method:"frappe.geo.country_info.get_country_timezone_info",
callback: function(data) {
erpnext.wiz.region.data = data.message;
erpnext.wiz.region.setup_fields(slide);
erpnext.wiz.region.bind_events(slide);
}
});
},
css_class: "single-column",
setup_fields: function(slide) {
var data = erpnext.wiz.region.data;
slide.get_input("country").empty()
.add_options([""].concat(keys(data.country_info).sort()));
slide.get_input("currency").empty()
.add_options(frappe.utils.unique([""].concat($.map(data.country_info,
function(opts, country) { return opts.currency; }))).sort());
slide.get_input("timezone").empty()
.add_options([""].concat(data.all_timezones));
if (data.default_country) {
slide.set_input("country", data.default_country);
}
},
bind_events: function(slide) {
slide.get_input("country").on("change", function() {
var country = slide.get_input("country").val();
var $timezone = slide.get_input("timezone");
onload: function(slide) {
frappe.call({
method:"frappe.geo.country_info.get_country_timezone_info",
callback: function(data) {
erpnext.wiz.region.data = data.message;
erpnext.wiz.region.setup_fields(slide);
erpnext.wiz.region.bind_events(slide);
}
});
},
css_class: "single-column",
setup_fields: function(slide) {
var data = erpnext.wiz.region.data;
$timezone.empty();
slide.get_input("country").empty()
.add_options([""].concat(keys(data.country_info).sort()));
// add country specific timezones first
if(country) {
var timezone_list = data.country_info[country].timezones || [];
$timezone.add_options(timezone_list.sort());
slide.get_field("currency").set_input(data.country_info[country].currency);
slide.get_field("currency").$input.trigger("change");
slide.get_input("currency").empty()
.add_options(frappe.utils.unique([""].concat($.map(data.country_info,
function(opts, country) { return opts.currency; }))).sort());
slide.get_input("timezone").empty()
.add_options([""].concat(data.all_timezones));
if (data.default_country) {
slide.set_input("country", data.default_country);
}
},
// add all timezones at the end, so that user has the option to change it to any timezone
$timezone.add_options([""].concat(data.all_timezones));
bind_events: function(slide) {
slide.get_input("country").on("change", function() {
var country = slide.get_input("country").val();
var $timezone = slide.get_input("timezone");
var data = erpnext.wiz.region.data;
slide.get_field("timezone").set_input($timezone.val());
$timezone.empty();
// temporarily set date format
frappe.boot.sysdefaults.date_format = (data.country_info[country].date_format
|| "dd-mm-yyyy");
});
slide.get_input("currency").on("change", function() {
var currency = slide.get_input("currency").val();
if (!currency) return;
frappe.model.with_doc("Currency", currency, function() {
frappe.provide("locals.:Currency." + currency);
var currency_doc = frappe.model.get_doc("Currency", currency);
var number_format = currency_doc.number_format;
if (number_format==="#.###") {
number_format = "#.###,##";
} else if (number_format==="#,###") {
number_format = "#,###.##"
// add country specific timezones first
if(country) {
var timezone_list = data.country_info[country].timezones || [];
$timezone.add_options(timezone_list.sort());
slide.get_field("currency").set_input(data.country_info[country].currency);
slide.get_field("currency").$input.trigger("change");
}
frappe.boot.sysdefaults.number_format = number_format;
locals[":Currency"][currency] = $.extend({}, currency_doc);
// add all timezones at the end, so that user has the option to change it to any timezone
$timezone.add_options([""].concat(data.all_timezones));
slide.get_field("timezone").set_input($timezone.val());
// temporarily set date format
frappe.boot.sysdefaults.date_format = (data.country_info[country].date_format
|| "dd-mm-yyyy");
});
});
}
},
user: {
title: __("The First User: You"),
icon: "icon-user",
fields: [
{"fieldname": "first_name", "label": __("First Name"), "fieldtype": "Data",
reqd:1},
{"fieldname": "last_name", "label": __("Last Name"), "fieldtype": "Data"},
{"fieldname": "email", "label": __("Email Address"), "fieldtype": "Data",
reqd:1, "description": __("You will use it to Login"), "options":"Email"},
{"fieldname": "password", "label": __("Password"), "fieldtype": "Password",
reqd:1},
{fieldtype:"Attach Image", fieldname:"attach_user",
label: __("Attach Your Picture")},
],
help: __('The first user will become the System Manager (you can change this later).'),
onload: function(slide) {
if(user!=="Administrator") {
slide.form.fields_dict.password.$wrapper.toggle(false);
slide.form.fields_dict.email.$wrapper.toggle(false);
slide.form.fields_dict.first_name.set_input(frappe.boot.user.first_name);
slide.form.fields_dict.last_name.set_input(frappe.boot.user.last_name);
slide.get_input("currency").on("change", function() {
var currency = slide.get_input("currency").val();
if (!currency) return;
frappe.model.with_doc("Currency", currency, function() {
frappe.provide("locals.:Currency." + currency);
var currency_doc = frappe.model.get_doc("Currency", currency);
var number_format = currency_doc.number_format;
if (number_format==="#.###") {
number_format = "#.###,##";
} else if (number_format==="#,###") {
number_format = "#,###.##"
}
var user_image = frappe.get_cookie("user_image");
if(user_image) {
var $attach_user = slide.form.fields_dict.attach_user.$wrapper;
$attach_user.find(".missing-image").toggle(false);
$attach_user.find("img").attr("src", decodeURIComponent(user_image)).toggle(true);
}
delete slide.form.fields_dict.email;
delete slide.form.fields_dict.password;
frappe.boot.sysdefaults.number_format = number_format;
locals[":Currency"][currency] = $.extend({}, currency_doc);
});
});
}
},
css_class: "single-column"
},
org: {
title: __("The Organization"),
icon: "icon-building",
fields: [
{fieldname:'company_name', label: __('Company Name'), fieldtype:'Data', reqd:1,
placeholder: __('e.g. "My Company LLC"')},
{fieldname:'company_abbr', label: __('Company Abbreviation'), fieldtype:'Data',
description: __('Max 5 characters'), placeholder: __('e.g. "MC"'), reqd:1},
{fieldname:'company_tagline', label: __('What does it do?'), fieldtype:'Data',
placeholder:__('e.g. "Build tools for builders"'), reqd:1},
{fieldname:'bank_account', label: __('Bank Account'), fieldtype:'Data',
placeholder: __('e.g. "XYZ National Bank"'), reqd:1 },
{fieldname:'chart_of_accounts', label: __('Chart of Accounts'),
options: "", fieldtype: 'Select'},
user: {
title: __("The First User: You"),
icon: "icon-user",
fields: [
{"fieldname": "first_name", "label": __("First Name"), "fieldtype": "Data",
reqd:1},
{"fieldname": "last_name", "label": __("Last Name"), "fieldtype": "Data"},
{"fieldname": "email", "label": __("Email Address"), "fieldtype": "Data",
reqd:1, "description": __("You will use it to Login"), "options":"Email"},
{"fieldname": "password", "label": __("Password"), "fieldtype": "Password",
reqd:1},
{fieldtype:"Attach Image", fieldname:"attach_user",
label: __("Attach Your Picture")},
],
help: __('The first user will become the System Manager (you can change this later).'),
onload: function(slide) {
if(user!=="Administrator") {
slide.form.fields_dict.password.$wrapper.toggle(false);
slide.form.fields_dict.email.$wrapper.toggle(false);
slide.form.fields_dict.first_name.set_input(frappe.boot.user.first_name);
slide.form.fields_dict.last_name.set_input(frappe.boot.user.last_name);
// TODO remove this
{fieldtype: "Section Break"},
{fieldname:'fy_start_date', label:__('Financial Year Start Date'), fieldtype:'Date',
description: __('Your financial year begins on'), reqd:1},
{fieldname:'fy_end_date', label:__('Financial Year End Date'), fieldtype:'Date',
description: __('Your financial year ends on'), reqd:1},
],
help: __('The name of your company for which you are setting up this system.'),
var user_image = frappe.get_cookie("user_image");
if(user_image) {
var $attach_user = slide.form.fields_dict.attach_user.$wrapper;
$attach_user.find(".missing-image").toggle(false);
$attach_user.find("img").attr("src", decodeURIComponent(user_image)).toggle(true);
}
onload: function(slide) {
erpnext.wiz.org.load_chart_of_accounts(slide);
erpnext.wiz.org.bind_events(slide);
erpnext.wiz.org.set_fy_dates(slide);
delete slide.form.fields_dict.email;
delete slide.form.fields_dict.password;
}
},
css_class: "single-column"
},
css_class: "single-column",
org: {
title: __("The Organization"),
icon: "icon-building",
fields: [
{fieldname:'company_name', label: __('Company Name'), fieldtype:'Data', reqd:1,
placeholder: __('e.g. "My Company LLC"')},
{fieldname:'company_abbr', label: __('Company Abbreviation'), fieldtype:'Data',
description: __('Max 5 characters'), placeholder: __('e.g. "MC"'), reqd:1},
{fieldname:'company_tagline', label: __('What does it do?'), fieldtype:'Data',
placeholder:__('e.g. "Build tools for builders"'), reqd:1},
{fieldname:'bank_account', label: __('Bank Account'), fieldtype:'Data',
placeholder: __('e.g. "XYZ National Bank"'), reqd:1 },
{fieldname:'chart_of_accounts', label: __('Chart of Accounts'),
options: "", fieldtype: 'Select'},
set_fy_dates: function(slide) {
var country = slide.wiz.get_values().country;
// TODO remove this
{fieldtype: "Section Break"},
{fieldname:'fy_start_date', label:__('Financial Year Start Date'), fieldtype:'Date',
description: __('Your financial year begins on'), reqd:1},
{fieldname:'fy_end_date', label:__('Financial Year End Date'), fieldtype:'Date',
description: __('Your financial year ends on'), reqd:1},
],
help: __('The name of your company for which you are setting up this system.'),
if(country) {
var fy = erpnext.wiz.fiscal_years[country];
var current_year = moment(new Date()).year();
var next_year = current_year + 1;
if(!fy) {
fy = ["01-01", "12-31"];
next_year = current_year;
onload: function(slide) {
erpnext.wiz.org.load_chart_of_accounts(slide);
erpnext.wiz.org.bind_events(slide);
erpnext.wiz.org.set_fy_dates(slide);
},
css_class: "single-column",
set_fy_dates: function(slide) {
var country = slide.wiz.get_values().country;
if(country) {
var fy = erpnext.wiz.fiscal_years[country];
var current_year = moment(new Date()).year();
var next_year = current_year + 1;
if(!fy) {
fy = ["01-01", "12-31"];
next_year = current_year;
}
slide.get_field("fy_start_date").set_input(current_year + "-" + fy[0]);
slide.get_field("fy_end_date").set_input(next_year + "-" + fy[1]);
}
slide.get_field("fy_start_date").set_input(current_year + "-" + fy[0]);
slide.get_field("fy_end_date").set_input(next_year + "-" + fy[1]);
}
},
},
load_chart_of_accounts: function(slide) {
var country = slide.wiz.get_values().country;
load_chart_of_accounts: function(slide) {
var country = slide.wiz.get_values().country;
if(country) {
frappe.call({
method: "erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts.get_charts_for_country",
args: {"country": country},
callback: function(r) {
if(r.message) {
slide.get_input("chart_of_accounts").empty()
.add_options(r.message);
if(country) {
frappe.call({
method: "erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts.get_charts_for_country",
args: {"country": country},
callback: function(r) {
if(r.message) {
slide.get_input("chart_of_accounts").empty()
.add_options(r.message);
if (r.message.length===1) {
var field = slide.get_field("chart_of_accounts");
field.set_value(r.message[0]);
field.df.hidden = 1;
field.refresh();
if (r.message.length===1) {
var field = slide.get_field("chart_of_accounts");
field.set_value(r.message[0]);
field.df.hidden = 1;
field.refresh();
}
}
}
}
})
}
},
bind_events: function(slide) {
slide.get_input("company_name").on("change", function() {
var parts = slide.get_input("company_name").val().split(" ");
var abbr = $.map(parts, function(p) { return p ? p.substr(0,1) : null }).join("");
slide.get_field("company_abbr").set_input(abbr.slice(0, 5).toUpperCase());
}).val(frappe.boot.sysdefaults.company_name || "").trigger("change");
slide.get_input("company_abbr").on("change", function() {
if(slide.get_input("company_abbr").val().length > 5) {
msgprint("Company Abbreviation cannot have more than 5 characters");
slide.get_field("company_abbr").set_input("");
})
}
});
// TODO remove this
slide.get_input("fy_start_date").on("change", function() {
var year_end_date =
frappe.datetime.add_days(frappe.datetime.add_months(
frappe.datetime.user_to_obj(slide.get_input("fy_start_date").val()), 12), -1);
slide.get_input("fy_end_date").val(frappe.datetime.obj_to_user(year_end_date));
});
}
},
branding: {
icon: "icon-bookmark",
title: __("The Brand"),
help: __('Upload your letter head and logo. (you can edit them later).'),
fields: [
{fieldtype:"Attach Image", fieldname:"attach_letterhead",
label: __("Attach Letterhead"),
description: __("Keep it web friendly 900px (w) by 100px (h)")
},
{fieldtype: "Column Break"},
{fieldtype:"Attach Image", fieldname:"attach_logo",
label:__("Attach Logo"),
description: __("100px by 100px")},
],
css_class: "two-column"
},
bind_events: function(slide) {
slide.get_input("company_name").on("change", function() {
var parts = slide.get_input("company_name").val().split(" ");
var abbr = $.map(parts, function(p) { return p ? p.substr(0,1) : null }).join("");
slide.get_field("company_abbr").set_input(abbr.slice(0, 5).toUpperCase());
}).val(frappe.boot.sysdefaults.company_name || "").trigger("change");
users: {
icon: "icon-money",
"title": __("Add Users"),
"help": __("Add users to your organization, other than yourself"),
"fields": [],
before_load: function(slide) {
slide.fields = [];
for(var i=1; i<5; i++) {
slide.fields = slide.fields.concat([
{fieldtype:"Section Break"},
{fieldtype:"Data", fieldname:"user_fullname_"+ i,
label:__("Full Name")},
{fieldtype:"Data", fieldname:"user_email_" + i,
label:__("Email ID"), placeholder:__("user@example.com"),
options: "Email"},
{fieldtype:"Column Break"},
{fieldtype: "Check", fieldname: "user_sales_" + i,
label:__("Sales"), default: 1},
{fieldtype: "Check", fieldname: "user_purchaser_" + i,
label:__("Purchaser"), default: 1},
{fieldtype: "Check", fieldname: "user_accountant_" + i,
label:__("Accountant"), default: 1},
]);
slide.get_input("company_abbr").on("change", function() {
if(slide.get_input("company_abbr").val().length > 5) {
msgprint("Company Abbreviation cannot have more than 5 characters");
slide.get_field("company_abbr").set_input("");
}
});
// TODO remove this
slide.get_input("fy_start_date").on("change", function() {
var year_end_date =
frappe.datetime.add_days(frappe.datetime.add_months(
frappe.datetime.user_to_obj(slide.get_input("fy_start_date").val()), 12), -1);
slide.get_input("fy_end_date").val(frappe.datetime.obj_to_user(year_end_date));
});
}
},
css_class: "two-column"
},
taxes: {
icon: "icon-money",
"title": __("Add Taxes"),
"help": __("List your tax heads (e.g. VAT, Customs etc; they should have unique names) and their standard rates. This will create a standard template, which you can edit and add more later."),
"fields": [],
before_load: function(slide) {
slide.fields = [];
for(var i=1; i<4; i++) {
slide.fields = slide.fields.concat([
{fieldtype:"Section Break"},
{fieldtype:"Data", fieldname:"tax_"+ i, label:__("Tax") + " " + i,
placeholder:__("e.g. VAT") + " " + i},
{fieldtype:"Column Break"},
{fieldtype:"Float", fieldname:"tax_rate_" + i, label:__("Rate (%)"), placeholder:__("e.g. 5")},
]);
}
branding: {
icon: "icon-bookmark",
title: __("The Brand"),
help: __('Upload your letter head and logo. (you can edit them later).'),
fields: [
{fieldtype:"Attach Image", fieldname:"attach_letterhead",
label: __("Attach Letterhead"),
description: __("Keep it web friendly 900px (w) by 100px (h)")
},
{fieldtype: "Column Break"},
{fieldtype:"Attach Image", fieldname:"attach_logo",
label:__("Attach Logo"),
description: __("100px by 100px")},
],
css_class: "two-column"
},
css_class: "two-column"
},
customers: {
icon: "icon-group",
"title": __("Your Customers"),
"help": __("List a few of your customers. They could be organizations or individuals."),
"fields": [],
before_load: function(slide) {
slide.fields = [];
for(var i=1; i<6; i++) {
slide.fields = slide.fields.concat([
{fieldtype:"Section Break"},
{fieldtype:"Data", fieldname:"customer_" + i, label:__("Customer") + " " + i,
placeholder:__("Customer Name")},
{fieldtype:"Column Break"},
{fieldtype:"Data", fieldname:"customer_contact_" + i,
label:__("Contact Name") + " " + i, placeholder:__("Contact Name")}
])
}
slide.fields[1].reqd = 1;
users: {
icon: "icon-money",
"title": __("Add Users"),
"help": __("Add users to your organization, other than yourself"),
"fields": [],
before_load: function(slide) {
slide.fields = [];
for(var i=1; i<5; i++) {
slide.fields = slide.fields.concat([
{fieldtype:"Section Break"},
{fieldtype:"Data", fieldname:"user_fullname_"+ i,
label:__("Full Name")},
{fieldtype:"Data", fieldname:"user_email_" + i,
label:__("Email ID"), placeholder:__("user@example.com"),
options: "Email"},
{fieldtype:"Column Break"},
{fieldtype: "Check", fieldname: "user_sales_" + i,
label:__("Sales"), default: 1},
{fieldtype: "Check", fieldname: "user_purchaser_" + i,
label:__("Purchaser"), default: 1},
{fieldtype: "Check", fieldname: "user_accountant_" + i,
label:__("Accountant"), default: 1},
]);
}
},
css_class: "two-column"
},
css_class: "two-column"
},
suppliers: {
icon: "icon-group",
"title": __("Your Suppliers"),
"help": __("List a few of your suppliers. They could be organizations or individuals."),
"fields": [],
before_load: function(slide) {
slide.fields = [];
for(var i=1; i<6; i++) {
slide.fields = slide.fields.concat([
{fieldtype:"Section Break"},
{fieldtype:"Data", fieldname:"supplier_" + i, label:__("Supplier")+" " + i,
placeholder:__("Supplier Name")},
{fieldtype:"Column Break"},
{fieldtype:"Data", fieldname:"supplier_contact_" + i,
label:__("Contact Name") + " " + i, placeholder:__("Contact Name")},
])
}
slide.fields[1].reqd = 1;
taxes: {
icon: "icon-money",
"title": __("Add Taxes"),
"help": __("List your tax heads (e.g. VAT, Customs etc; they should have unique names) and their standard rates. This will create a standard template, which you can edit and add more later."),
"fields": [],
before_load: function(slide) {
slide.fields = [];
for(var i=1; i<4; i++) {
slide.fields = slide.fields.concat([
{fieldtype:"Section Break"},
{fieldtype:"Data", fieldname:"tax_"+ i, label:__("Tax") + " " + i,
placeholder:__("e.g. VAT") + " " + i},
{fieldtype:"Column Break"},
{fieldtype:"Float", fieldname:"tax_rate_" + i, label:__("Rate (%)"), placeholder:__("e.g. 5")},
]);
}
},
css_class: "two-column"
},
css_class: "two-column"
},
items: {
icon: "icon-barcode",
"title": __("Your Products or Services"),
"help": __("List your products or services that you buy or sell. Make sure to check the Item Group, Unit of Measure and other properties when you start."),
"fields": [],
before_load: function(slide) {
slide.fields = [];
for(var i=1; i<6; i++) {
slide.fields = slide.fields.concat([
{fieldtype:"Section Break", show_section_border: true},
{fieldtype:"Data", fieldname:"item_" + i, label:__("Item") + " " + i,
placeholder:__("A Product or Service")},
{fieldtype:"Select", label:__("Group"), fieldname:"item_group_" + i,
options:[__("Products"), __("Services"),
__("Raw Material"), __("Consumable"), __("Sub Assemblies")],
"default": __("Products")},
{fieldtype:"Select", fieldname:"item_uom_" + i, label:__("UOM"),
options:[__("Unit"), __("Nos"), __("Box"), __("Pair"), __("Kg"), __("Set"),
__("Hour"), __("Minute")],
"default": __("Unit")},
{fieldtype: "Check", fieldname: "is_sales_item_" + i, label:__("We sell this Item"), default: 1},
{fieldtype: "Check", fieldname: "is_purchase_item_" + i, label:__("We buy this Item")},
{fieldtype:"Column Break"},
{fieldtype:"Currency", fieldname:"item_price_" + i, label:__("Rate")},
{fieldtype:"Attach Image", fieldname:"item_img_" + i, label:__("Attach Image")},
])
}
slide.fields[1].reqd = 1;
// dummy data
slide.fields.push({fieldtype: "Section Break"});
slide.fields.push({fieldtype: "Check", fieldname: "add_sample_data",
label: __("Add a few sample records"), "default": 1});
customers: {
icon: "icon-group",
"title": __("Your Customers"),
"help": __("List a few of your customers. They could be organizations or individuals."),
"fields": [],
before_load: function(slide) {
slide.fields = [];
for(var i=1; i<6; i++) {
slide.fields = slide.fields.concat([
{fieldtype:"Section Break"},
{fieldtype:"Data", fieldname:"customer_" + i, label:__("Customer") + " " + i,
placeholder:__("Customer Name")},
{fieldtype:"Column Break"},
{fieldtype:"Data", fieldname:"customer_contact_" + i,
label:__("Contact Name") + " " + i, placeholder:__("Contact Name")}
])
}
slide.fields[1].reqd = 1;
},
css_class: "two-column"
},
css_class: "two-column"
},
});
// Source: https://en.wikipedia.org/wiki/Fiscal_year
// default 1st Jan - 31st Dec
suppliers: {
icon: "icon-group",
"title": __("Your Suppliers"),
"help": __("List a few of your suppliers. They could be organizations or individuals."),
"fields": [],
before_load: function(slide) {
slide.fields = [];
for(var i=1; i<6; i++) {
slide.fields = slide.fields.concat([
{fieldtype:"Section Break"},
{fieldtype:"Data", fieldname:"supplier_" + i, label:__("Supplier")+" " + i,
placeholder:__("Supplier Name")},
{fieldtype:"Column Break"},
{fieldtype:"Data", fieldname:"supplier_contact_" + i,
label:__("Contact Name") + " " + i, placeholder:__("Contact Name")},
])
}
slide.fields[1].reqd = 1;
},
css_class: "two-column"
},
erpnext.wiz.fiscal_years = {
"Afghanistan": ["12-20", "12-21"],
"Australia": ["07-01", "06-30"],
"Bangladesh": ["07-01", "06-30"],
"Canada": ["04-01", "03-31"],
"Costa Rica": ["10-01", "09-30"],
"Egypt": ["07-01", "06-30"],
"Hong Kong": ["04-01", "03-31"],
"India": ["04-01", "03-31"],
"Iran": ["06-23", "06-22"],
"Italy": ["07-01", "06-30"],
"Myanmar": ["04-01", "03-31"],
"New Zealand": ["04-01", "03-31"],
"Pakistan": ["07-01", "06-30"],
"Singapore": ["04-01", "03-31"],
"South Africa": ["03-01", "02-28"],
"Thailand": ["10-01", "09-30"],
"United Kingdom": ["04-01", "03-31"],
}
items: {
icon: "icon-barcode",
"title": __("Your Products or Services"),
"help": __("List your products or services that you buy or sell. Make sure to check the Item Group, Unit of Measure and other properties when you start."),
"fields": [],
before_load: function(slide) {
slide.fields = [];
for(var i=1; i<6; i++) {
slide.fields = slide.fields.concat([
{fieldtype:"Section Break", show_section_border: true},
{fieldtype:"Data", fieldname:"item_" + i, label:__("Item") + " " + i,
placeholder:__("A Product or Service")},
{fieldtype:"Select", label:__("Group"), fieldname:"item_group_" + i,
options:[__("Products"), __("Services"),
__("Raw Material"), __("Consumable"), __("Sub Assemblies")],
"default": __("Products")},
{fieldtype:"Select", fieldname:"item_uom_" + i, label:__("UOM"),
options:[__("Unit"), __("Nos"), __("Box"), __("Pair"), __("Kg"), __("Set"),
__("Hour"), __("Minute")],
"default": __("Unit")},
{fieldtype: "Check", fieldname: "is_sales_item_" + i, label:__("We sell this Item"), default: 1},
{fieldtype: "Check", fieldname: "is_purchase_item_" + i, label:__("We buy this Item")},
{fieldtype:"Column Break"},
{fieldtype:"Currency", fieldname:"item_price_" + i, label:__("Rate")},
{fieldtype:"Attach Image", fieldname:"item_img_" + i, label:__("Attach Image")},
])
}
slide.fields[1].reqd = 1;
// dummy data
slide.fields.push({fieldtype: "Section Break"});
slide.fields.push({fieldtype: "Check", fieldname: "add_sample_data",
label: __("Add a few sample records"), "default": 1});
},
css_class: "two-column"
},
});
// Source: https://en.wikipedia.org/wiki/Fiscal_year
// default 1st Jan - 31st Dec
erpnext.wiz.fiscal_years = {
"Afghanistan": ["12-20", "12-21"],
"Australia": ["07-01", "06-30"],
"Bangladesh": ["07-01", "06-30"],
"Canada": ["04-01", "03-31"],
"Costa Rica": ["10-01", "09-30"],
"Egypt": ["07-01", "06-30"],
"Hong Kong": ["04-01", "03-31"],
"India": ["04-01", "03-31"],
"Iran": ["06-23", "06-22"],
"Italy": ["07-01", "06-30"],
"Myanmar": ["04-01", "03-31"],
"New Zealand": ["04-01", "03-31"],
"Pakistan": ["07-01", "06-30"],
"Singapore": ["04-01", "03-31"],
"South Africa": ["03-01", "02-28"],
"Thailand": ["10-01", "09-30"],
"United Kingdom": ["04-01", "03-31"],
};
};
frappe.wiz.on("before_load", function() {
load_erpnext_slides();
frappe.wiz.add_slide(erpnext.wiz.user);
frappe.wiz.add_slide(erpnext.wiz.org);
frappe.wiz.add_slide(erpnext.wiz.branding);

View File

@ -56,13 +56,15 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_bank_entry);
}
// stop
if(flt(doc.per_delivered, 2) < 100 || flt(doc.per_billed) < 100) {
cur_frm.add_custom_button(__('Stop'), this.stop_sales_order)
}
if (this.frm.has_perm("submit")) {
// stop
if(flt(doc.per_delivered, 2) < 100 || flt(doc.per_billed) < 100) {
cur_frm.add_custom_button(__('Stop'), this.stop_sales_order)
}
cur_frm.add_custom_button(__('Close'), this.close_sales_order)
cur_frm.add_custom_button(__('Close'), this.close_sales_order)
}
// maintenance
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)===-1) {
@ -82,8 +84,10 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
} else {
// un-stop
cur_frm.add_custom_button(__('Re-open'), cur_frm.cscript['Unstop Sales Order']);
if (this.frm.has_perm("submit")) {
// un-stop
cur_frm.add_custom_button(__('Re-open'), cur_frm.cscript['Unstop Sales Order']);
}
}
}

View File

@ -27,6 +27,7 @@ def get_random_quote():
("There is more to life than increasing its speed.", "Mahatma Gandhi"),
("A small body of determined spirits fired by an unquenchable faith in their mission can alter the course of history.", "Mahatma Gandhi"),
("If two wrongs don't make a right, try three.", "Laurence J. Peter"),
("Inspiration exists, but it has to find you working.", "Pablo Picasso"),
]
return random.choice(quotes)

View File

@ -1,167 +1,168 @@
[
{
"allow_comments": 0,
"allow_delete": 0,
"allow_edit": 1,
"allow_multiple": 1,
"breadcrumbs": null,
"doc_type": "Address",
"docstatus": 0,
"doctype": "Web Form",
"introduction_text": null,
"login_required": 1,
"modified": "2015-06-01 06:53:43.699336",
"name": "addresses",
"page_name": "addresses",
"published": 1,
"success_message": null,
"success_url": "/addresses",
"title": "Addresses",
"allow_comments": 0,
"allow_delete": 0,
"allow_edit": 1,
"allow_multiple": 1,
"breadcrumbs": null,
"doc_type": "Address",
"docstatus": 0,
"doctype": "Web Form",
"introduction_text": null,
"is_standard": 1,
"login_required": 1,
"modified": "2015-06-01 06:53:43.699336",
"name": "addresses",
"page_name": "addresses",
"published": 1,
"success_message": null,
"success_url": "/addresses",
"title": "Addresses",
"web_form_fields": [
{
"default": null,
"description": "",
"fieldname": "address_title",
"fieldtype": "Data",
"hidden": 0,
"label": "Address Title",
"options": null,
"read_only": 0,
"default": null,
"description": "",
"fieldname": "address_title",
"fieldtype": "Data",
"hidden": 0,
"label": "Address Title",
"options": null,
"read_only": 0,
"reqd": 0
},
},
{
"default": null,
"description": null,
"fieldname": "address_type",
"fieldtype": "Select",
"hidden": 0,
"label": "Address Type",
"options": "Billing\nShipping\nOffice\nPersonal\nPlant\nPostal\nShop\nSubsidiary\nWarehouse\nOther",
"read_only": 0,
"default": null,
"description": null,
"fieldname": "address_type",
"fieldtype": "Select",
"hidden": 0,
"label": "Address Type",
"options": "Billing\nShipping\nOffice\nPersonal\nPlant\nPostal\nShop\nSubsidiary\nWarehouse\nOther",
"read_only": 0,
"reqd": 1
},
},
{
"default": null,
"description": null,
"fieldname": "address_line1",
"fieldtype": "Data",
"hidden": 0,
"label": "Address Line 1",
"options": null,
"read_only": 0,
"default": null,
"description": null,
"fieldname": "address_line1",
"fieldtype": "Data",
"hidden": 0,
"label": "Address Line 1",
"options": null,
"read_only": 0,
"reqd": 1
},
},
{
"default": null,
"description": null,
"fieldname": "address_line2",
"fieldtype": "Data",
"hidden": 0,
"label": "Address Line 2",
"options": null,
"read_only": 0,
"default": null,
"description": null,
"fieldname": "address_line2",
"fieldtype": "Data",
"hidden": 0,
"label": "Address Line 2",
"options": null,
"read_only": 0,
"reqd": 0
},
},
{
"default": null,
"description": null,
"fieldname": "city",
"fieldtype": "Data",
"hidden": 0,
"label": "City/Town",
"options": null,
"read_only": 0,
"default": null,
"description": null,
"fieldname": "city",
"fieldtype": "Data",
"hidden": 0,
"label": "City/Town",
"options": null,
"read_only": 0,
"reqd": 1
},
},
{
"default": null,
"description": null,
"fieldname": "state",
"fieldtype": "Data",
"hidden": 0,
"label": "State",
"options": null,
"read_only": 0,
"default": null,
"description": null,
"fieldname": "state",
"fieldtype": "Data",
"hidden": 0,
"label": "State",
"options": null,
"read_only": 0,
"reqd": 0
},
},
{
"default": null,
"description": null,
"fieldname": "pincode",
"fieldtype": "Data",
"hidden": 0,
"label": "Pincode",
"options": null,
"read_only": 0,
"default": null,
"description": null,
"fieldname": "pincode",
"fieldtype": "Data",
"hidden": 0,
"label": "Pincode",
"options": null,
"read_only": 0,
"reqd": 0
},
},
{
"default": null,
"description": null,
"fieldname": "country",
"fieldtype": "Data",
"hidden": 0,
"label": "Country",
"options": "Country",
"read_only": 0,
"default": null,
"description": null,
"fieldname": "country",
"fieldtype": "Data",
"hidden": 0,
"label": "Country",
"options": "Country",
"read_only": 0,
"reqd": 1
},
},
{
"default": null,
"description": null,
"fieldname": null,
"fieldtype": "Column Break",
"hidden": null,
"label": null,
"options": null,
"read_only": null,
"default": null,
"description": null,
"fieldname": null,
"fieldtype": "Column Break",
"hidden": null,
"label": null,
"options": null,
"read_only": null,
"reqd": null
},
},
{
"default": null,
"description": null,
"fieldname": "email_id",
"fieldtype": "Data",
"hidden": 0,
"label": "Email Id",
"options": null,
"read_only": 0,
"default": null,
"description": null,
"fieldname": "email_id",
"fieldtype": "Data",
"hidden": 0,
"label": "Email Id",
"options": null,
"read_only": 0,
"reqd": 0
},
},
{
"default": null,
"description": null,
"fieldname": "phone",
"fieldtype": "Data",
"hidden": 0,
"label": "Phone",
"options": null,
"read_only": 0,
"default": null,
"description": null,
"fieldname": "phone",
"fieldtype": "Data",
"hidden": 0,
"label": "Phone",
"options": null,
"read_only": 0,
"reqd": 1
},
},
{
"default": "0",
"description": "",
"fieldname": "is_primary_address",
"fieldtype": "Check",
"hidden": 0,
"label": "Preferred Billing Address",
"options": null,
"read_only": 0,
"default": "0",
"description": "",
"fieldname": "is_primary_address",
"fieldtype": "Check",
"hidden": 0,
"label": "Preferred Billing Address",
"options": null,
"read_only": 0,
"reqd": 0
},
},
{
"default": "0",
"description": "",
"fieldname": "is_shipping_address",
"fieldtype": "Check",
"hidden": 0,
"label": "Preferred Shipping Address",
"options": null,
"read_only": 0,
"default": "0",
"description": "",
"fieldname": "is_shipping_address",
"fieldtype": "Check",
"hidden": 0,
"label": "Preferred Shipping Address",
"options": null,
"read_only": 0,
"reqd": 0
}
],
],
"web_page_link_text": null
}
]
]

View File

@ -1,68 +1,69 @@
[
{
"allow_comments": 1,
"allow_delete": 1,
"allow_edit": 1,
"allow_multiple": 1,
"breadcrumbs": "[{\"title\":\"Issues\", \"name\":\"issues\"}]",
"doc_type": "Issue",
"docstatus": 0,
"doctype": "Web Form",
"introduction_text": null,
"login_required": 1,
"modified": "2015-06-01 08:14:26.350792",
"name": "issues",
"page_name": "issues",
"published": 1,
"success_message": "",
"success_url": "/issues",
"title": "Issues",
"allow_comments": 1,
"allow_delete": 1,
"allow_edit": 1,
"allow_multiple": 1,
"breadcrumbs": "[{\"title\":\"Issues\", \"name\":\"issues\"}]",
"doc_type": "Issue",
"docstatus": 0,
"doctype": "Web Form",
"is_standard": 1,
"introduction_text": null,
"login_required": 1,
"modified": "2015-06-01 08:14:26.350792",
"name": "issues",
"page_name": "issues",
"published": 1,
"success_message": "",
"success_url": "/issues",
"title": "Issues",
"web_form_fields": [
{
"default": null,
"description": null,
"fieldname": "subject",
"fieldtype": "Data",
"hidden": 0,
"label": "Subject",
"options": null,
"read_only": 0,
"default": null,
"description": null,
"fieldname": "subject",
"fieldtype": "Data",
"hidden": 0,
"label": "Subject",
"options": null,
"read_only": 0,
"reqd": 1
},
},
{
"default": "Open",
"description": null,
"fieldname": "status",
"fieldtype": "Select",
"hidden": null,
"label": "Status",
"options": "Open\nReplied\nHold\nClosed",
"read_only": 1,
"default": "Open",
"description": null,
"fieldname": "status",
"fieldtype": "Select",
"hidden": null,
"label": "Status",
"options": "Open\nReplied\nHold\nClosed",
"read_only": 1,
"reqd": 0
},
},
{
"default": null,
"description": null,
"fieldname": "description",
"fieldtype": "Text",
"hidden": 0,
"label": "Description",
"options": null,
"read_only": 0,
"default": null,
"description": null,
"fieldname": "description",
"fieldtype": "Text",
"hidden": 0,
"label": "Description",
"options": null,
"read_only": 0,
"reqd": 0
},
},
{
"default": null,
"description": null,
"fieldname": "attachment",
"fieldtype": "Attach",
"hidden": null,
"label": "Attachment",
"options": null,
"read_only": null,
"default": null,
"description": null,
"fieldname": "attachment",
"fieldtype": "Attach",
"hidden": null,
"label": "Attachment",
"options": null,
"read_only": null,
"reqd": null
}
],
],
"web_page_link_text": null
}
]
]

View File

@ -18,7 +18,7 @@
<br>
<hr>
<h3>Next Steps</h3>
<h3>{%= __("Next Steps") %}</h3>
<ul class="list-unstyled">
<li><a class="text-muted" href="#">{%= __("Go to the Desktop and start using ERPNext") %}</a></li>
<li><a class="text-muted" href="#Module/Learn">{%= __("View a list of all the help videos") %}</a></li>

View File

@ -46,13 +46,14 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
if (cint(frappe.defaults.get_default("auto_accounting_for_stock"))) {
this.show_general_ledger();
}
if(doc.status !== "Closed") {
cur_frm.add_custom_button(__("Close"), this.close_delivery_note)
if (this.frm.has_perm("submit") && (doc.status !== "Closed")
&& this.frm.doc.__onload && this.frm.doc.__onload.has_return_entry) {
cur_frm.add_custom_button(__("Close"), this.close_delivery_note)
}
}
if(doc.__onload && !doc.__onload.billing_complete && doc.docstatus==1 && !doc.is_return && doc.status!="Closed") {
if(doc.__onload && !doc.__onload.billing_complete && doc.docstatus==1
&& !doc.is_return && doc.status!="Closed") {
// show Make Invoice button only if Delivery Note is not created from Sales Invoice
var from_sales_invoice = false;
from_sales_invoice = cur_frm.doc.items.some(function(item) {
@ -63,7 +64,7 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
cur_frm.add_custom_button(__('Invoice'), this.make_sales_invoice).addClass("btn-primary");
}
if(doc.docstatus==1 && doc.status === "Closed") {
if(doc.docstatus==1 && doc.status === "Closed" && this.frm.has_perm("submit")) {
cur_frm.add_custom_button(__('Re-open'), this.reopen_delivery_note)
}
erpnext.stock.delivery_note.set_print_hide(doc, dt, dn);

View File

@ -69,7 +69,10 @@ class DeliveryNote(SellingController):
where docstatus=1 and delivery_note=%s""", self.name)
if billed_qty:
total_qty = sum((item.qty for item in self.get("items")))
self.get("__onload").billing_complete = (billed_qty[0][0] == total_qty)
self.set_onload("billing_complete", (billed_qty[0][0] == total_qty))
self.set_onload("has_return_entry", len(frappe.db.exists({"doctype": "Delivery Note",
"is_return": 1, "return_against": self.name, "docstatus": 1})))
def before_print(self):
def toggle_print_hide(meta, fieldname):

View File

@ -105,7 +105,11 @@ class Item(WebsiteGenerator):
frappe.local.message_log.pop()
except requests.exceptions.HTTPError:
frappe.msgprint(_("Warning: Invalid Attachment {0}").format(self.website_image))
frappe.msgprint(_("Warning: Invalid attachment {0}").format(self.website_image))
self.website_image = None
except requests.exceptions.SSLError:
frappe.msgprint(_("Warning: Invalid SSL certificate on attachment {0}").format(self.website_image))
self.website_image = None
# for CSV import

View File

@ -57,14 +57,18 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend
if(this.frm.doc.docstatus == 1 && this.frm.doc.status!="Closed") {
cur_frm.add_custom_button(__('Return'), this.make_purchase_return);
if(this.frm.doc.__onload && !this.frm.doc.__onload.billing_complete) {
cur_frm.add_custom_button(__('Invoice'), this.make_purchase_invoice).addClass("btn-primary");
cur_frm.add_custom_button(__('Invoice'),
this.make_purchase_invoice).addClass("btn-primary");
}
if (this.frm.has_perm("submit") &&
this.frm.doc.__onload && this.frm.doc.__onload.has_return_entry) {
cur_frm.add_custom_button(__("Close"), this.close_purchase_receipt)
}
cur_frm.add_custom_button(__("Close"), this.close_purchase_receipt)
}
}
if(this.frm.doc.docstatus==1 && this.frm.doc.status === "Closed") {
if(this.frm.doc.docstatus==1 && this.frm.doc.status === "Closed" && this.frm.has_perm("submit")) {
cur_frm.add_custom_button(__('Re-open'), this.reopen_purchase_receipt)
}

View File

@ -51,7 +51,10 @@ class PurchaseReceipt(BuyingController):
where purchase_receipt=%s and docstatus=1""", self.name)
if billed_qty:
total_qty = sum((item.qty for item in self.get("items")))
self.get("__onload").billing_complete = (billed_qty[0][0] == total_qty)
self.set_onload("billing_complete", (billed_qty[0][0] == total_qty))
self.set_onload("has_return_entry", len(frappe.db.exists({"doctype": "Purchase Receipt",
"is_return": 1, "return_against": self.name, "docstatus": 1})))
def validate(self):
super(PurchaseReceipt, self).validate()

View File

@ -1,5 +1,5 @@
<div class="web-list-item">
<a href="/addresses?name={{ doc.name }}" no-pjax class="no-decoration">
<a href="/addresses?name={{ doc.name | urlencode }}" no-pjax class="no-decoration">
<h4 class="strong">{{ doc.address_title }}</h4>
<p class="text-muted small">
{{ frappe.get_doc(doc).get_display() }}

View File

@ -1,6 +1,6 @@
from setuptools import setup, find_packages
version = "6.8.4"
version = "6.9.0"
with open("requirements.txt", "r") as f:
install_requires = f.readlines()

View File

@ -1 +1 @@
erpnext
erpnext

View File

@ -1 +1 @@
en english
en english

View File

@ -9,4 +9,4 @@
"run_selenium_tests": 1,
"host_name": "http://localhost:8000",
"install_apps": ["erpnext"]
}
}