Merge remote-tracking branch 'frappe/develop' into develop
This commit is contained in:
commit
c40b99be26
@ -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."))
|
||||
|
@ -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'],
|
||||
]
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
1
erpnext/change_log/current/sms.md
Normal file
1
erpnext/change_log/current/sms.md
Normal file
@ -0,0 +1 @@
|
||||
- Now system will give SMS delivery message and maintain a log
|
@ -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"),
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
}
|
||||
]
|
||||
}
|
@ -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';
|
@ -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()
|
||||
|
@ -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'])
|
||||
|
||||
|
@ -9,6 +9,5 @@ Manufacturing
|
||||
Stock
|
||||
Support
|
||||
Utilities
|
||||
Contacts
|
||||
Shopping Cart
|
||||
Hub Node
|
||||
|
@ -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")
|
@ -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",
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
117
erpnext/setup/page/setup_wizard/sample_data.py
Normal file
117
erpnext/setup/page/setup_wizard/sample_data.py
Normal 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)
|
@ -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"],
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
||||
}
|
||||
|
@ -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"},
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -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"):
|
||||
|
@ -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
|
@ -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",
|
||||
|
Loading…
x
Reference in New Issue
Block a user