Merge remote-tracking branch 'frappe/develop' into develop

This commit is contained in:
Tsutomu Mimori 2015-07-23 22:10:40 +09:00
commit c40b99be26
29 changed files with 519 additions and 234 deletions

View File

@ -49,7 +49,7 @@ class Account(Document):
self.root_type = par.root_type
def validate_root_details(self):
#does not exists parent
# does not exists parent
if frappe.db.exists("Account", self.name):
if not frappe.db.get_value("Account", self.name, "parent_account"):
throw(_("Root cannot be edited."))

View File

@ -17,7 +17,7 @@ erpnext.accounts.CostCenterController = frappe.ui.form.Controller.extend({
return {
filters:[
['Account', 'company', '=', me.frm.doc.company],
['Account', 'report_type', '=', 'Profit and Loss'],
['Account', 'root_type', '=', 'Expense'],
['Account', 'is_group', '=', '0'],
]
}

View File

@ -3,9 +3,7 @@
from __future__ import unicode_literals
import frappe
from frappe import msgprint, _
from frappe import _
from frappe.utils.nestedset import NestedSet
class CostCenter(NestedSet):
@ -14,18 +12,46 @@ class CostCenter(NestedSet):
def autoname(self):
self.name = self.cost_center_name.strip() + ' - ' + \
frappe.db.get_value("Company", self.company, "abbr")
def validate(self):
self.validate_mandatory()
self.validate_accounts()
def validate_mandatory(self):
if self.cost_center_name != self.company and not self.parent_cost_center:
msgprint(_("Please enter parent cost center"), raise_exception=1)
frappe.throw(_("Please enter parent cost center"))
elif self.cost_center_name == self.company and self.parent_cost_center:
msgprint(_("Root cannot have a parent cost center"), raise_exception=1)
frappe.throw(_("Root cannot have a parent cost center"))
def validate_accounts(self):
if self.is_group==1 and self.get("budgets"):
frappe.throw(_("Budget cannot be set for Group Cost Center"))
check_acc_list = []
for d in self.get('budgets'):
if d.account:
account_details = frappe.db.get_value("Account", d.account,
["is_group", "company", "root_type"], as_dict=1)
if account_details.is_group:
frappe.throw(_("Budget cannot be assigned against Group Account {0}").format(d.account))
elif account_details.company != self.company:
frappe.throw(_("Account {0} does not belongs to company {1}").format(d.account, self.company))
elif account_details.root_type != "Expense":
frappe.throw(_("Budget cannot be assigned against {0}, as it's not an Expense account")
.format(d.account))
if [d.account, d.fiscal_year] in check_acc_list:
frappe.throw(_("Account {0} has been entered more than once for fiscal year {1}")
.format(d.account, d.fiscal_year))
else:
check_acc_list.append([d.account, d.fiscal_year])
def convert_group_to_ledger(self):
if self.check_if_child_exists():
msgprint(_("Cannot convert Cost Center to ledger as it has child nodes"), raise_exception=1)
frappe.throw(_("Cannot convert Cost Center to ledger as it has child nodes"))
elif self.check_gle_exists():
msgprint(_("Cost Center with existing transactions can not be converted to ledger"), raise_exception=1)
frappe.throw(_("Cost Center with existing transactions can not be converted to ledger"))
else:
self.is_group = 0
self.save()
@ -33,7 +59,7 @@ class CostCenter(NestedSet):
def convert_ledger_to_group(self):
if self.check_gle_exists():
msgprint(_("Cost Center with existing transactions can not be converted to group"), raise_exception=1)
frappe.throw(_("Cost Center with existing transactions can not be converted to group"))
else:
self.is_group = 1
self.save()
@ -46,21 +72,6 @@ class CostCenter(NestedSet):
return frappe.db.sql("select name from `tabCost Center` where \
parent_cost_center = %s and docstatus != 2", self.name)
def validate_budget_details(self):
check_acc_list = []
for d in self.get('budgets'):
if self.is_group==1:
msgprint(_("Budget cannot be set for Group Cost Centers"), raise_exception=1)
if [d.account, d.fiscal_year] in check_acc_list:
msgprint(_("Account {0} has been entered more than once for fiscal year {1}").format(d.account, d.fiscal_year), raise_exception=1)
else:
check_acc_list.append([d.account, d.fiscal_year])
def validate(self):
self.validate_mandatory()
self.validate_budget_details()
def before_rename(self, olddn, newdn, merge=False):
# Add company abbr if not provided
from erpnext.setup.doctype.company.company import get_name_with_abbr

View File

@ -4,10 +4,9 @@
from __future__ import unicode_literals
from frappe.model.document import Document
from erpnext.controllers.accounts_controller import validate_taxes_and_charges, validate_inclusive_tax
from erpnext.accounts.doctype.sales_taxes_and_charges_template.sales_taxes_and_charges_template \
import valdiate_taxes_and_charges_template
class PurchaseTaxesandChargesTemplate(Document):
def validate(self):
for tax in self.get("taxes"):
validate_taxes_and_charges(tax)
validate_inclusive_tax(tax, self)
valdiate_taxes_and_charges_template(self)

View File

@ -5,21 +5,25 @@ from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from erpnext.controllers.accounts_controller import validate_taxes_and_charges, validate_inclusive_tax
from frappe.utils.nestedset import get_root_of
class SalesTaxesandChargesTemplate(Document):
def validate(self):
if self.is_default == 1:
frappe.db.sql("""update `tabSales Taxes and Charges Template`
set is_default = 0
where ifnull(is_default,0) = 1
and name != %s and company = %s""",
(self.name, self.company))
valdiate_taxes_and_charges_template(self)
# at least one territory
self.validate_table_has_rows("territories")
def valdiate_taxes_and_charges_template(doc):
if not doc.is_default and not frappe.get_all(doc.doctype, filters={"is_default": 1}):
doc.is_default = 1
for tax in self.get("taxes"):
validate_taxes_and_charges(tax)
validate_inclusive_tax(tax, self)
if doc.is_default == 1:
frappe.db.sql("""update `tab{0}` set is_default = 0
where ifnull(is_default,0) = 1 and name != %s and company = %s""".format(doc.doctype),
(doc.name, doc.company))
if doc.meta.get_field("territories"):
if not doc.territories:
doc.append("territories", {"territory": get_root_of("Territory") })
for tax in doc.get("taxes"):
validate_taxes_and_charges(tax)
validate_inclusive_tax(tax, doc)

View File

@ -0,0 +1 @@
- Now system will give SMS delivery message and maintain a log

View File

@ -42,6 +42,11 @@ def get_data():
"name": "SMS Center",
"description":_("Send mass SMS to your contacts"),
},
{
"type": "doctype",
"name": "SMS Log",
"description":_("Logs for maintaining sms delivery status"),
}
]
},
{

View File

@ -48,6 +48,11 @@ def get_data():
"name": "SMS Center",
"description":_("Send mass SMS to your contacts"),
},
{
"type": "doctype",
"name": "SMS Log",
"description":_("Logs for maintaining sms delivery status"),
},
{
"type": "doctype",
"name": "Newsletter",

View File

@ -1,92 +0,0 @@
{
"allow_rename": 1,
"autoname": "field:party_type_name",
"creation": "2014-04-07 12:32:18.010384",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Master",
"fields": [
{
"fieldname": "party_type_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Party Type Name",
"permlevel": 0,
"reqd": 1
},
{
"fieldname": "parent_party_type",
"fieldtype": "Link",
"label": "Parent Party Type",
"options": "Party Type",
"permlevel": 0
},
{
"default": "Yes",
"fieldname": "allow_children",
"fieldtype": "Select",
"label": "Allow Children",
"options": "Yes\nNo",
"permlevel": 0
},
{
"fieldname": "default_price_list",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Default Price List",
"options": "Price List",
"permlevel": 0
},
{
"fieldname": "lft",
"fieldtype": "Int",
"hidden": 1,
"label": "LFT",
"permlevel": 0,
"read_only": 1,
"search_index": 1
},
{
"fieldname": "rgt",
"fieldtype": "Int",
"hidden": 1,
"label": "RGT",
"permlevel": 0,
"read_only": 1,
"search_index": 1
},
{
"fieldname": "old_parent",
"fieldtype": "Data",
"hidden": 1,
"label": "Old Parent",
"permlevel": 0,
"read_only": 1
}
],
"modified": "2015-02-05 05:11:42.046004",
"modified_by": "Administrator",
"module": "Contacts",
"name": "Party Type",
"owner": "Administrator",
"permissions": [
{
"apply_user_permissions": 1,
"create": 1,
"permlevel": 0,
"read": 1,
"role": "Sales User",
"share": 1,
"write": 1
},
{
"apply_user_permissions": 1,
"create": 1,
"permlevel": 0,
"read": 1,
"role": "Purchase User",
"share": 1,
"write": 1
}
]
}

View File

@ -1,9 +0,0 @@
# 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.utils.nestedset import NestedSet
class PartyType(NestedSet):
nsm_parent_field = 'parent_party_type';

View File

@ -335,12 +335,15 @@ def get_item_details(item):
res = frappe.db.sql("""select stock_uom, description
from `tabItem` where (ifnull(end_of_life, "0000-00-00")="0000-00-00" or end_of_life > now())
and name=%s""", item, as_dict=1)
if not res:
return {}
res = res[0]
res["bom_no"] = frappe.db.get_value("BOM", filters={"item": item, "is_default": 1})
if not res["bom_no"]:
variant_of= frappe.db.get_value("Item", item, "variant_of")
if variant_of:
res["bom_no"] = frappe.db.get_value("BOM", filters={"item": variant_of, "is_default": 1})
return res
@frappe.whitelist()

View File

@ -9,6 +9,7 @@ from frappe import msgprint, _
from frappe.model.document import Document
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
from erpnext.manufacturing.doctype.production_order.production_order import get_item_details
class ProductionPlanningTool(Document):
def __init__(self, arg1, arg2=None):
@ -27,16 +28,7 @@ class ProductionPlanningTool(Document):
return ret
def get_item_details(self, item_code):
""" Pull other item details from item master"""
item = frappe.db.sql("""select description, stock_uom, default_bom
from `tabItem` where name = %s""", item_code, as_dict =1)
ret = {
'description' : item and item[0]['description'],
'stock_uom' : item and item[0]['stock_uom'],
'bom_no' : item and item[0]['default_bom']
}
return ret
return get_item_details(item_code)
def clear_so_table(self):
self.set('sales_orders', [])
@ -142,15 +134,14 @@ class ProductionPlanningTool(Document):
self.clear_item_table()
for p in items:
item_details = frappe.db.sql("""select description, stock_uom, default_bom
from tabItem where name=%s""", p['item_code'])
item_details = get_item_details(p['item_code'])
pi = self.append('items', {})
pi.sales_order = p['parent']
pi.warehouse = p['warehouse']
pi.item_code = p['item_code']
pi.description = item_details and item_details[0][0] or ''
pi.stock_uom = item_details and item_details[0][1] or ''
pi.bom_no = item_details and item_details[0][2] or ''
pi.description = item_details and item_details.description or ''
pi.stock_uom = item_details and item_details.stock_uom or ''
pi.bom_no = item_details and item_details.bom_no or ''
pi.so_pending_qty = flt(p['pending_qty'])
pi.planned_qty = flt(p['pending_qty'])

View File

@ -9,6 +9,5 @@ Manufacturing
Stock
Support
Utilities
Contacts
Shopping Cart
Hub Node

View File

@ -177,3 +177,4 @@ erpnext.patches.v5_1.track_operations
execute:frappe.rename_doc("DocType", "Salary Manager", "Process Payroll", force=True)
erpnext.patches.v5_1.rename_roles
erpnext.patches.v5_1.default_bom
execute:frappe.delete_doc("DocType", "Party Type")

View File

@ -272,6 +272,10 @@ def make_material_request(source_name, target_doc=None):
def postprocess(source, doc):
doc.material_request_type = "Purchase"
so = frappe.get_doc("Sales Order", source_name)
item_table = "Packed Item" if so.packed_items else "Sales Order Item"
doc = get_mapped_doc("Sales Order", source_name, {
"Sales Order": {
"doctype": "Material Request",
@ -279,7 +283,7 @@ def make_material_request(source_name, target_doc=None):
"docstatus": ["=", 1]
}
},
"Sales Order Item": {
item_table: {
"doctype": "Material Request Item",
"field_map": {
"parent": "sales_order_no",

View File

@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe
from frappe import _, throw, msgprint
from frappe.utils import cstr, nowdate
from frappe.utils import nowdate
from frappe.model.document import Document
@ -63,8 +63,7 @@ def send_sms(receiver_list, msg, sender_name = ''):
}
if frappe.db.get_value('SMS Settings', None, 'sms_gateway_url'):
ret = send_via_gateway(arg)
msgprint(ret)
send_via_gateway(arg)
else:
msgprint(_("Please Update SMS Settings"))
@ -74,12 +73,17 @@ def send_via_gateway(arg):
for d in ss.get("parameters"):
args[d.parameter] = d.value
resp = []
success_list = []
for d in arg.get('receiver_list'):
args[ss.receiver_parameter] = d
resp.append(send_request(ss.sms_gateway_url, args))
status = send_request(ss.sms_gateway_url, args)
if status == 200:
success_list.append(d)
return resp
if len(success_list) > 0:
args.update(arg)
create_sms_log(args, success_list)
frappe.msgprint(_("SMS sent to following numbers: {0}").format("\n" + "\n".join(success_list)))
# Send Request
# =========================================================
@ -90,11 +94,8 @@ def send_request(gateway_url, args):
headers = {}
headers['Accept'] = "text/plain, text/html, */*"
conn.request('GET', api_url + urllib.urlencode(args), headers = headers) # send request
resp = conn.getresponse() # get response
resp = resp.read()
if resp.status==200:
create_sms_log()
return resp
resp = conn.getresponse() # get response
return resp.status
# Split gateway url to server and api url
# =========================================================
@ -109,12 +110,13 @@ def scrub_gateway_url(url):
# Create SMS Log
# =========================================================
def create_sms_log(arg, sent_sms):
sl = frappe.get_doc('SMS Log')
sl.sender_name = arg['sender_name']
def create_sms_log(args, sent_to):
sl = frappe.new_doc('SMS Log')
sl.sender_name = args['sender_name']
sl.sent_on = nowdate()
sl.receiver_list = cstr(arg['receiver_list'])
sl.message = arg['message']
sl.no_of_requested_sms = len(arg['receiver_list'])
sl.no_of_sent_sms = sent_sms
sl.message = args['message']
sl.no_of_requested_sms = len(args['receiver_list'])
sl.requested_numbers = "\n".join(args['receiver_list'])
sl.no_of_sent_sms = len(sent_to)
sl.sent_to = "\n".join(sent_to)
sl.save()

View File

@ -183,4 +183,4 @@ def install(country=None):
parent_link_field = ("parent_" + scrub(doc.doctype))
if doc.meta.get_field(parent_link_field) and not doc.get(parent_link_field):
doc.flags.ignore_mandatory = True
doc.insert()
doc.insert(ignore_permissions=True)

View File

@ -0,0 +1,117 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from frappe.utils.make_random import add_random_children, get_random
import frappe.utils
def make_sample_data():
"""Create a few opportunities, quotes, material requests, issues, todos, projects
to help the user get started"""
selling_items = frappe.get_all("Item", filters = {"is_sales_item": "Yes"})
buying_items = frappe.get_all("Item", filters = {"is_sales_item": "No"})
if selling_items:
for i in range(3):
make_opportunity(selling_items)
make_quote(selling_items)
make_projects()
if buying_items:
make_material_request(buying_items)
frappe.db.commit()
def make_opportunity(selling_items):
b = frappe.get_doc({
"doctype": "Opportunity",
"enquiry_from": "Customer",
"customer": get_random("Customer"),
"enquiry_type": "Sales",
"with_items": 1
})
add_random_children(b, "items", rows=len(selling_items), randomize = {
"qty": (1, 5),
"item_code": ("Item", {"is_sales_item": "Yes"})
}, unique="item_code")
b.insert(ignore_permissions=True)
b.add_comment("This is a dummy record")
def make_quote(selling_items):
qtn = frappe.get_doc({
"doctype": "Quotation",
"quotation_to": "Customer",
"customer": get_random("Customer"),
"order_type": "Sales"
})
add_random_children(qtn, "items", rows=len(selling_items), randomize = {
"qty": (1, 5),
"item_code": ("Item", {"is_sales_item": "Yes"})
}, unique="item_code")
qtn.insert(ignore_permissions=True)
qtn.add_comment("This is a dummy record")
def make_material_request(buying_items):
for i in buying_items:
mr = frappe.get_doc({
"doctype": "Material Request",
"material_request_type": "Purchase",
"items": [{
"schedule_date": frappe.utils.add_days(frappe.utils.nowdate(), 7),
"item_code": i.name,
"qty": 10
}]
})
mr.insert()
mr.submit()
mr.add_comment("This is a dummy record")
def make_issue():
pass
def make_projects():
project = frappe.get_doc({
"doctype": "Project",
"project_name": "ERPNext Implementation",
})
current_date = frappe.utils.nowdate()
project.set("tasks", [
{
"title": "Explore ERPNext",
"start_date": frappe.utils.add_days(current_date, 1),
"end_date": frappe.utils.add_days(current_date, 2)
},
{
"title": "Run Sales Cycle",
"start_date": frappe.utils.add_days(current_date, 2),
"end_date": frappe.utils.add_days(current_date, 3)
},
{
"title": "Run Billing Cycle",
"start_date": frappe.utils.add_days(current_date, 3),
"end_date": frappe.utils.add_days(current_date, 4)
},
{
"title": "Run Purchase Cycle",
"start_date": frappe.utils.add_days(current_date, 4),
"end_date": frappe.utils.add_days(current_date, 5)
},
{
"title": "Go Live!",
"start_date": frappe.utils.add_days(current_date, 5),
"end_date": frappe.utils.add_days(current_date, 6)
}])
project.insert(ignore_permissions=True)

View File

@ -25,6 +25,7 @@ frappe.pages['setup-wizard'].on_page_load = function(wrapper) {
erpnext.wiz.user.slide,
erpnext.wiz.org.slide,
erpnext.wiz.branding.slide,
erpnext.wiz.users.slide,
erpnext.wiz.taxes.slide,
erpnext.wiz.customers.slide,
erpnext.wiz.suppliers.slide,
@ -137,7 +138,7 @@ erpnext.wiz.WizardSlide = Class.extend({
});
this.form.make();
} else {
$(this.body).html(this.html)
$(this.body).html(this.html);
}
if(this.id > 0) {
@ -412,11 +413,30 @@ $.extend(erpnext.wiz, {
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]);
}
},
load_chart_of_accounts: function(slide) {
var country = slide.wiz.get_values().country;
@ -486,11 +506,41 @@ $.extend(erpnext.wiz, {
},
},
users: {
slide: {
icon: "icon-money",
"title": __("Add Users"),
"help": __("Add users to your organization"),
"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"
},
},
taxes: {
slide: {
icon: "icon-money",
"title": __("Add Taxes"),
"help": __("List your tax heads (e.g. VAT, Excise; they should have unique names) and their standard rates. This will create a standard template, which you can edit and add more later."),
"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 = [];
@ -526,6 +576,7 @@ $.extend(erpnext.wiz, {
label:__("Contact Name") + " " + i, placeholder:__("Contact Name")}
])
}
slide.fields[1].reqd = 1;
},
css_class: "two-column"
},
@ -549,6 +600,7 @@ $.extend(erpnext.wiz, {
label:__("Contact Name") + " " + i, placeholder:__("Contact Name")},
])
}
slide.fields[1].reqd = 1;
},
css_class: "two-column"
},
@ -578,9 +630,11 @@ $.extend(erpnext.wiz, {
{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;
},
css_class: "two-column"
},
@ -627,3 +681,25 @@ $.extend(erpnext.wiz, {
},
});
// 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"],
}

View File

@ -2,7 +2,7 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe, json
import frappe, json, copy
from frappe.utils import cstr, flt, getdate
from frappe import _
@ -13,6 +13,7 @@ from frappe.geo.country_info import get_country_info
from frappe.utils.nestedset import get_root_of
from .default_website import website_maker
import install_fixtures
from .sample_data import make_sample_data
@frappe.whitelist()
def setup_account(args=None):
@ -38,6 +39,9 @@ def setup_account(args=None):
create_fiscal_year_and_company(args)
frappe.local.message_log = []
create_users(args)
frappe.local.message_log = []
set_defaults(args)
frappe.local.message_log = []
@ -81,6 +85,7 @@ def setup_account(args=None):
frappe.clear_cache()
make_sample_data()
except:
if args:
traceback = frappe.get_traceback()
@ -297,21 +302,45 @@ def create_taxes(args):
tax_group = frappe.db.get_value("Account", {"company": args.get("company_name"),
"is_group": 1, "account_type": "Tax", "root_type": "Liability"})
if tax_group:
frappe.get_doc({
"doctype":"Account",
"company": args.get("company_name").strip(),
"parent_account": tax_group,
"account_name": args.get("tax_" + str(i)),
"is_group": 0,
"report_type": "Balance Sheet",
"account_type": "Tax",
"tax_rate": flt(tax_rate) if tax_rate else None
}).insert()
account = make_tax_head(args, i, tax_group, tax_rate)
make_sales_and_purchase_tax_templates(account)
except frappe.NameError, e:
if e.args[2][0]==1062:
pass
else:
raise
def make_tax_head(args, i, tax_group, tax_rate):
return frappe.get_doc({
"doctype":"Account",
"company": args.get("company_name").strip(),
"parent_account": tax_group,
"account_name": args.get("tax_" + str(i)),
"is_group": 0,
"report_type": "Balance Sheet",
"account_type": "Tax",
"tax_rate": flt(tax_rate) if tax_rate else None
}).insert(ignore_permissions=True)
def make_sales_and_purchase_tax_templates(account):
doc = {
"doctype": "Sales Taxes and Charges Template",
"title": account.name,
"taxes": [{
"category": "Valuation and Total",
"charge_type": "On Net Total",
"account_head": account.name,
"description": "{0} @ {1}".format(account.account_name, account.tax_rate),
"rate": account.tax_rate
}]
}
# Sales
frappe.get_doc(copy.deepcopy(doc)).insert()
# Purchase
doc["doctype"] = "Purchase Taxes and Charges Template"
frappe.get_doc(copy.deepcopy(doc)).insert()
def create_items(args):
for i in xrange(1,6):
@ -349,9 +378,30 @@ def create_items(args):
filename, filetype, content = item_image
fileurl = save_file(filename, content, "Item", item, decode=True).file_url
frappe.db.set_value("Item", item, "image", fileurl)
if args.get("item_price_" + str(i)):
item_price = flt(args.get("item_price_" + str(i)))
if is_sales_item:
price_list_name = frappe.db.get_value("Price List", {"selling": 1})
make_item_price(item, price_list_name, item_price)
if is_purchase_item:
price_list_name = frappe.db.get_value("Price List", {"buying": 1})
make_item_price(item, price_list_name, item_price)
except frappe.NameError:
pass
def make_item_price(item, price_list_name, item_price):
frappe.get_doc({
"doctype": "Item Price",
"price_list": price_list_name,
"item_code": item,
"price_list_rate": item_price
}).insert()
def create_customers(args):
for i in xrange(1,6):
customer = args.get("customer_" + str(i))
@ -367,13 +417,8 @@ def create_customers(args):
}).insert()
if args.get("customer_contact_" + str(i)):
contact = args.get("customer_contact_" + str(i)).split(" ")
frappe.get_doc({
"doctype":"Contact",
"customer": customer,
"first_name":contact[0],
"last_name": len(contact) > 1 and contact[1] or ""
}).insert()
create_contact(args.get("customer_contact_" + str(i)),
"customer", customer)
except frappe.NameError:
pass
@ -390,16 +435,21 @@ def create_suppliers(args):
}).insert()
if args.get("supplier_contact_" + str(i)):
contact = args.get("supplier_contact_" + str(i)).split(" ")
frappe.get_doc({
"doctype":"Contact",
"supplier": supplier,
"first_name":contact[0],
"last_name": len(contact) > 1 and contact[1] or ""
}).insert()
create_contact(args.get("supplier_contact_" + str(i)),
"supplier", supplier)
except frappe.NameError:
pass
def create_contact(contact, party_type, party):
"""Create contact based on given contact name"""
contact = contact.strip().split(" ")
frappe.get_doc({
"doctype":"Contact",
party_type: party,
"first_name":contact[0],
"last_name": len(contact) > 1 and contact[1] or ""
}).insert()
def create_letter_head(args):
if args.get("attach_letterhead"):
@ -451,6 +501,60 @@ def login_as_first_user(args):
if args.get("email") and hasattr(frappe.local, "login_manager"):
frappe.local.login_manager.login_as(args.get("email"))
def create_users(args):
# create employee for self
emp = frappe.get_doc({
"doctype": "Employee",
"full_name": " ".join(filter(None, [args.get("first_name"), args.get("last_name")])),
"user_id": frappe.session.user,
"status": "Active",
"company": args.get("company_name")
})
emp.flags.ignore_mandatory = True
emp.insert(ignore_permissions = True)
for i in xrange(1,5):
email = args.get("user_email_" + str(i))
fullname = args.get("user_fullname_" + str(i))
if email:
if not fullname:
fullname = email.split("@")[0]
parts = fullname.split(" ", 1)
user = frappe.get_doc({
"doctype": "User",
"email": email,
"first_name": parts[0],
"last_name": parts[1] if len(parts) > 1 else "",
"enabled": 1,
"user_type": "System User"
})
# default roles
user.append_roles("Projects User", "Stock User", "Support Team")
if args.get("user_sales_" + str(i)):
user.append_roles("Sales User", "Sales Manager", "Accounts User")
if args.get("user_purchaser_" + str(i)):
user.append_roles("Purchase User", "Purchase Manager", "Accounts User")
if args.get("user_accountant_" + str(i)):
user.append_roles("Accounts Manager", "Accounts User")
user.flags.delay_emails = True
user.insert(ignore_permissions=True)
# create employee
emp = frappe.get_doc({
"doctype": "Employee",
"full_name": fullname,
"user_id": user.name,
"status": "Active",
"company": args.get("company_name")
})
emp.flags.ignore_mandatory = True
emp.insert(ignore_permissions = True)
@frappe.whitelist()
def load_messages(language):
frappe.clear_cache()

View File

@ -51,4 +51,15 @@ args = {
"timezone": "America/New_York",
"password": "password",
"email": "test@erpnext.com",
"user_email_1": "testsetup1@example.com",
"user_fullname_1": "test setup user",
"user_sales_1": 1,
"user_purchaser_1": 1,
"user_accountant_1": 1,
"user_email_1": "testsetup2@example.com",
"user_fullname_1": "test setup user",
"user_sales_2": 1,
"user_purchaser_2": 0,
"user_accountant_2": 0
}

View File

@ -10,6 +10,7 @@ def get_notification_config():
"Issue": {"status": "Open"},
"Warranty Claim": {"status": "Open"},
"Task": {"status": "Open"},
"Project": {"status": "Open"},
"Lead": {"status": "Open"},
"Contact": {"status": "Open"},
"Opportunity": {"status": "Open"},

View File

@ -86,8 +86,12 @@ frappe.ui.form.on("Item", {
},
manage_variants: function(frm) {
frappe.route_options = {"item_code": frm.doc.name };
frappe.set_route("List", "Manage Variants");
if (cur_frm.doc.__unsaved==1) {
frappe.throw(__("You have unsaved changes. Please save."))
} else {
frappe.route_options = {"item_code": frm.doc.name };
frappe.set_route("List", "Manage Variants");
}
}
});

View File

@ -325,7 +325,8 @@ class Item(WebsiteGenerator):
for d in variants:
update_variant(self.name, d)
updated.append(d.item_code)
frappe.msgprint(_("Item Variants {0} updated").format(", ".join(updated)))
if updated:
frappe.msgprint(_("Item Variants {0} updated").format(", ".join(updated)))
def validate_has_variants(self):
if not self.has_variants and frappe.db.get_value("Item", self.name, "has_variants"):

View File

@ -9,8 +9,13 @@ def execute(filters=None):
columns = get_columns()
sl_entries = get_stock_ledger_entries(filters)
item_details = get_item_details(filters)
opening_row = get_opening_balance(filters, columns)
data = []
if opening_row:
data.append(opening_row)
for sle in sl_entries:
item_detail = item_details[sle.item_code]
@ -20,7 +25,7 @@ def execute(filters=None):
(sle.incoming_rate if sle.actual_qty > 0 else 0.0),
sle.valuation_rate, sle.stock_value, sle.voucher_type, sle.voucher_no,
sle.batch_no, sle.serial_no, sle.company])
return columns, data
def get_columns():
@ -40,7 +45,7 @@ def get_stock_ledger_entries(filters):
where company = %(company)s and
posting_date between %(from_date)s and %(to_date)s
{sle_conditions}
order by posting_date desc, posting_time desc, name desc"""\
order by posting_date asc, posting_time asc, name asc"""\
.format(sle_conditions=get_sle_conditions(filters)), filters, as_dict=1)
def get_item_details(filters):
@ -73,3 +78,22 @@ def get_sle_conditions(filters):
conditions.append("voucher_no=%(voucher_no)s")
return "and {}".format(" and ".join(conditions)) if conditions else ""
def get_opening_balance(filters, columns):
if not (filters.item_code and filters.warehouse and filters.from_date):
return
from erpnext.stock.stock_ledger import get_previous_sle
last_entry = get_previous_sle({
"item_code": filters.item_code,
"warehouse": filters.warehouse,
"posting_date": filters.from_date,
"posting_time": "00:00:00"
})
row = [""]*len(columns)
row[1] = _("'Opening'")
for i, v in ((9, 'qty_after_transaction'), (11, 'valuation_rate'), (12, 'stock_value')):
row[i] = last_entry.get(v, 0)
return row

View File

@ -1,32 +1,58 @@
{
"autoname": "SMSLOG/.########",
"creation": "2012-03-27 14:36:47.000000",
"creation": "2012-03-27 14:36:47",
"docstatus": 0,
"doctype": "DocType",
"fields": [
{
"fieldname": "column_break0",
"fieldtype": "Column Break",
"permlevel": 0,
"width": "50%"
},
{
"fieldname": "sender_name",
"fieldtype": "Data",
"label": "Sender Name",
"permlevel": 0
"permlevel": 0,
"read_only": 1
},
{
"fieldname": "sent_on",
"fieldtype": "Date",
"label": "Sent On",
"permlevel": 0
"permlevel": 0,
"read_only": 1
},
{
"fieldname": "receiver_list",
"fieldname": "column_break0",
"fieldtype": "Column Break",
"permlevel": 0,
"read_only": 0,
"width": "50%"
},
{
"fieldname": "message",
"fieldtype": "Small Text",
"label": "Receiver List",
"permlevel": 0
"label": "Message",
"permlevel": 0,
"read_only": 1
},
{
"fieldname": "sec_break1",
"fieldtype": "Section Break",
"options": "Simple",
"permlevel": 0,
"precision": "",
"read_only": 0
},
{
"fieldname": "no_of_requested_sms",
"fieldtype": "Int",
"label": "No of Requested SMS",
"permlevel": 0,
"read_only": 1
},
{
"fieldname": "requested_numbers",
"fieldtype": "Small Text",
"label": "Requested Numbers",
"permlevel": 0,
"read_only": 1
},
{
"fieldname": "column_break1",
@ -34,28 +60,25 @@
"permlevel": 0,
"width": "50%"
},
{
"fieldname": "no_of_requested_sms",
"fieldtype": "Int",
"label": "No of Requested SMS",
"permlevel": 0
},
{
"fieldname": "no_of_sent_sms",
"fieldtype": "Int",
"label": "No of Sent SMS",
"permlevel": 0
"permlevel": 0,
"read_only": 1
},
{
"fieldname": "message",
"fieldname": "sent_to",
"fieldtype": "Small Text",
"label": "Message",
"permlevel": 0
"label": "Sent To",
"permlevel": 0,
"precision": "",
"read_only": 1
}
],
"icon": "icon-mobile-phone",
"idx": 1,
"modified": "2013-12-20 19:24:35.000000",
"modified": "2015-07-22 11:53:25.998578",
"modified_by": "Administrator",
"module": "Utilities",
"name": "SMS Log",