Merge branch 'develop' of https://github.com/frappe/erpnext.git into Item-Tax-Template-V12

# Conflicts:
#	erpnext/buying/doctype/purchase_order/purchase_order.json
#	erpnext/patches.txt
#	erpnext/selling/doctype/quotation/quotation.json
#	erpnext/selling/doctype/sales_order/sales_order.json
This commit is contained in:
Saif Ur Rehman 2019-01-22 16:20:40 +05:00
commit 74a3370095
41 changed files with 3179 additions and 2019 deletions

View File

@ -33,17 +33,16 @@ before_script:
- cd ~/frappe-bench
- bench get-app erpnext $TRAVIS_BUILD_DIR
- bench use test_site
- bench reinstall --mariadb-root-username root --mariadb-root-password travis --yes
- bench build
- bench scheduler disable
- sed -i 's/9000/9001/g' sites/common_site_config.json
- bench start &
- sleep 10
jobs:
include:
- stage: test
script:
- bench reinstall --mariadb-root-username root --mariadb-root-password travis --yes
- bench scheduler disable
- sed -i 's/9000/9001/g' sites/common_site_config.json
- bench start &
- sleep 10
- set -e
- bench run-tests --app erpnext --coverage
after_script:

View File

@ -5,7 +5,7 @@ import frappe
from erpnext.hooks import regional_overrides
from frappe.utils import getdate
__version__ = '10.1.77'
__version__ = '10.1.78'
def get_default_company(user=None):
'''Get default company for user'''

View File

@ -250,10 +250,12 @@ def get_serial_no_data(pos_profile, company):
cond = "1=1"
if pos_profile.get('update_stock') and pos_profile.get('warehouse'):
cond = "warehouse = '{0}'".format(pos_profile.get('warehouse'))
cond = "warehouse = %(warehouse)s"
serial_nos = frappe.db.sql("""select name, warehouse, item_code from `tabSerial No` where {0}
and company = %(company)s """.format(cond), {'company': company}, as_dict=1)
serial_nos = frappe.db.sql("""select name, warehouse, item_code
from `tabSerial No` where {0} and company = %(company)s """.format(cond),{
'company': company, 'warehouse': frappe.db.escape(pos_profile.get('warehouse'))
}, as_dict=1)
itemwise_serial_no = {}
for sn in serial_nos:

View File

@ -1135,18 +1135,15 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
},
apply_category: function() {
frappe.db.get_value("Item Group", {lft: 1, is_group: 1}, "name", (r) => {
category = this.selected_item_group || r.name;
if(category == r.name) {
return this.item_data
} else {
return this.item_data.filter(function(element, index, array){
return element.item_group == category;
});
}
})
var me = this;
category = this.selected_item_group || "All Item Groups";
if(category == 'All Item Groups') {
return this.item_data
} else {
return this.item_data.filter(function(element, index, array){
return element.item_group == category;
});
}
},
bind_items_event: function() {

View File

@ -0,0 +1,64 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
/* eslint-disable */
frappe.query_reports["Account Balance"] = {
"filters": [
{
fieldname:"company",
label: __("Company"),
fieldtype: "Link",
options: "Company",
default: frappe.defaults.get_user_default("Company")
},
{
fieldname:"report_date",
label: __("Date"),
fieldtype: "Date",
default: frappe.datetime.get_today(),
reqd: 1
},
{
fieldname: "root_type",
label: __("Root Type"),
fieldtype: "Select",
options: [
{ "value": "Asset", "label": __("Asset") },
{ "value": "Liability", "label": __("Liability") },
{ "value": "Income", "label": __("Income") },
{ "value": "Expense", "label": __("Expense") },
{ "value": "Equity", "label": __("Equity") }
],
},
{
fieldname: "account_type",
label: __("Account Type"),
fieldtype: "Select",
options: [
{ "value": "Accumulated Depreciation", "label": __("Accumulated Depreciation") },
{ "value": "Asset Received But Not Billed", "label": __("Asset Received But Not Billed") },
{ "value": "Bank", "label": __("Bank") },
{ "value": "Cash", "label": __("Cash") },
{ "value": "Chargeble", "label": __("Chargeble") },
{ "value": "Capital Work in Progress", "label": __("Capital Work in Progress") },
{ "value": "Cost of Goods Sold", "label": __("Cost of Goods Sold") },
{ "value": "Depreciation", "label": __("Depreciation") },
{ "value": "Equity", "label": __("Equity") },
{ "value": "Expense Account", "label": __("Expense Account") },
{ "value": "Expenses Included In Asset Valuation", "label": __("Expenses Included In Asset Valuation") },
{ "value": "Expenses Included In Valuation", "label": __("Expenses Included In Valuation") },
{ "value": "Fixed Asset", "label": __("Fixed Asset") },
{ "value": "Income Account", "label": __("Income Account") },
{ "value": "Payable", "label": __("Payable") },
{ "value": "Receivable", "label": __("Receivable") },
{ "value": "Round Off", "label": __("Round Off") },
{ "value": "Stock", "label": __("Stock") },
{ "value": "Stock Adjustment", "label": __("Stock Adjustment") },
{ "value": "Stock Received But Not Billed", "label": __("Stock Received But Not Billed") },
{ "value": "Tax", "label": __("Tax") },
{ "value": "Temporary", "label": __("Temporary") },
],
},
]
}

View File

@ -0,0 +1,19 @@
{
"add_total_row": 0,
"creation": "2019-01-02 18:01:46.691685",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"modified": "2019-01-02 18:01:46.691685",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Account Balance",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Bank Account",
"report_name": "Account Balance",
"report_type": "Script Report",
"roles": []
}

View File

@ -0,0 +1,73 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
from erpnext.accounts.utils import get_balance_on
def execute(filters=None):
filters = frappe._dict(filters or {})
columns = get_columns(filters)
data = get_data(filters)
return columns, data
def get_columns(filters):
columns = [
{
"label": _("Account"),
"fieldtype": "Link",
"fieldname": "account",
"options": "Account",
"width": 100
},
{
"label": _("Currency"),
"fieldtype": "Link",
"fieldname": "currency",
"options": "Currency",
"hidden": 1,
"width": 50
},
{
"label": _("Balance"),
"fieldtype": "Currency",
"fieldname": "balance",
"options": "currency",
"width": 100
}
]
return columns
def get_conditions(filters):
conditions = {}
if filters.account_type:
conditions["account_type"] = filters.account_type
return conditions
if filters.company:
conditions["company"] = filters.company
if filters.root_type:
conditions["root_type"] = filters.root_type
return conditions
def get_data(filters):
data = []
conditions = get_conditions(filters)
accounts = frappe.db.get_all("Account", fields=["name", "account_currency"],
filters=conditions)
for d in accounts:
balance = get_balance_on(d.name, date=filters.report_date)
row = {"account": d.name, "balance": balance, "currency": d.account_currency}
data.append(row)
return data

View File

@ -0,0 +1,69 @@
from __future__ import unicode_literals
import frappe
import unittest
from frappe.utils import getdate
from erpnext.accounts.report.account_balance.account_balance import execute
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
class TestAccountBalance(unittest.TestCase):
def test_account_balance(self):
frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company 2'")
frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 2'")
filters = {
'company': '_Test Company 2',
'report_date': getdate(),
'root_type': 'Income',
}
make_sales_invoice()
report = execute(filters)
expected_data = [
{
"account": 'Sales - _TC2',
"currency": 'EUR',
"balance": -100.0,
},
{
"account": 'Income - _TC2',
"currency": 'EUR',
"balance": -100.0,
},
{
"account": 'Service - _TC2',
"currency": 'EUR',
"balance": 0.0,
},
{
"account": 'Direct Income - _TC2',
"currency": 'EUR',
"balance": -100.0,
},
{
"account": 'Indirect Income - _TC2',
"currency": 'EUR',
"balance": 0.0,
},
]
self.assertEqual(expected_data, report[1])
def make_sales_invoice():
frappe.set_user("Administrator")
create_sales_invoice(company="_Test Company 2",
customer = '_Test Customer 2',
currency = 'EUR',
warehouse = 'Finished Goods - _TC2',
debit_to = 'Debtors - _TC2',
income_account = 'Sales - _TC2',
expense_account = 'Cost of Goods Sold - _TC2',
cost_center = '_Test Company 2 - _TC2')

View File

@ -190,7 +190,7 @@ class ReceivablePayableReport(object):
dn_details = get_dn_details(args.get("party_type"), voucher_nos)
self.voucher_details = get_voucher_details(args.get("party_type"), voucher_nos, dn_details)
if self.filters.based_on_payment_terms:
if self.filters.based_on_payment_terms and gl_entries_data:
self.payment_term_map = self.get_payment_term_detail(voucher_nos)
for gle in gl_entries_data:

View File

@ -180,7 +180,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
def get_voucherwise_data(self, party_naming_by, args):
voucherwise_data = ReceivablePayableReport(self.filters).run(args)[1]
cols = ["posting_date", "party"]
cols = ["posting_date", "party", "customer-contact"]
if party_naming_by == "Naming Series":
cols += ["party_name"]

View File

@ -298,7 +298,7 @@
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"in_standard_filter": 1,
"label": "Date",
"length": 0,
"no_copy": 0,

View File

@ -14,6 +14,7 @@ from erpnext.stock import get_warehouse_account_map
class QualityInspectionRequiredError(frappe.ValidationError): pass
class QualityInspectionRejectedError(frappe.ValidationError): pass
class QualityInspectionNotSubmittedError(frappe.ValidationError): pass
class StockController(AccountsController):
def validate(self):
@ -338,18 +339,20 @@ class StockController(AccountsController):
qa_required = True
elif self.doctype == "Stock Entry" and not d.quality_inspection and d.t_warehouse:
qa_required = True
if self.docstatus == 1 and d.quality_inspection:
qa_doc = frappe.get_doc("Quality Inspection", d.quality_inspection)
if qa_doc.docstatus == 0:
link = frappe.utils.get_link_to_form('Quality Inspection', d.quality_inspection)
frappe.throw(_("Quality Inspection: {0} is not submitted for the item: {1} in row {2}").format(link, d.item_code, d.idx), QualityInspectionNotSubmittedError)
if qa_required:
qa_failed = any([r.status=="Rejected" for r in qa_doc.readings])
if qa_failed:
frappe.throw(_("Row {0}: Quality Inspection rejected for item {1}")
.format(d.idx, d.item_code), QualityInspectionRejectedError)
elif qa_required :
frappe.msgprint(_("Quality Inspection required for Item {0}").format(d.item_code))
if self.docstatus==1:
raise QualityInspectionRequiredError
elif self.docstatus == 1:
if d.quality_inspection:
qa_doc = frappe.get_doc("Quality Inspection", d.quality_inspection)
qa_failed = any([r.status=="Rejected" for r in qa_doc.readings])
if qa_failed:
frappe.throw(_("Row {0}: Quality Inspection rejected for item {1}")
.format(d.idx, d.item_code), QualityInspectionRejectedError)
def update_blanket_order(self):
@ -428,4 +431,4 @@ def get_voucherwise_gl_entries(future_stock_vouchers, posting_date):
tuple([posting_date] + [d[1] for d in future_stock_vouchers]), as_dict=1):
gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d)
return gl_entries
return gl_entries

View File

@ -12,7 +12,7 @@ app_license = "GNU General Public License (v3)"
source_link = "https://github.com/frappe/erpnext"
develop_version = '12.x.x-develop'
staging_version = '11.0.3-beta.34'
staging_version = '11.0.3-beta.35'
error_report_email = "support@erpnext.com"
@ -232,12 +232,16 @@ doc_events = {
}
scheduler_events = {
"all": [
"erpnext.projects.doctype.project.project.project_status_update_reminder"
],
"hourly": [
'erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group.trigger_emails',
"erpnext.accounts.doctype.subscription.subscription.process_all",
"erpnext.erpnext_integrations.doctype.amazon_mws_settings.amazon_mws_settings.schedule_get_order_details",
"erpnext.accounts.doctype.gl_entry.gl_entry.rename_gle_sle_docs",
"erpnext.projects.doctype.project.project.hourly_reminder",
"erpnext.projects.doctype.project.project.collect_project_status"
],
"daily": [
"erpnext.stock.reorder_item.reorder_item",
@ -257,6 +261,7 @@ scheduler_events = {
"erpnext.assets.doctype.asset.asset.make_post_gl_entry",
"erpnext.crm.doctype.contract.contract.update_status_for_contracts",
"erpnext.projects.doctype.project.project.update_project_sales_billing",
"erpnext.projects.doctype.project.project.send_project_status_email_to_users",
"erpnext.quality_management.doctype.quality_review.quality_review.review"
],
"daily_long": [

View File

@ -112,6 +112,10 @@ def get_user_emails_from_group(group):
if isinstance(group_doc, string_types):
group_doc = frappe.get_doc('Daily Work Summary Group', group)
emails = [d.email for d in group_doc.users if frappe.db.get_value("User", d.user, "enabled")]
emails = get_users_email(group_doc)
return emails
def get_users_email(doc):
return [d.email for d in doc.users
if frappe.db.get_value("User", d.user, "enabled")]

View File

@ -350,7 +350,8 @@ frappe.ui.form.on("Work Order", {
before_submit: function(frm) {
frm.toggle_reqd(["fg_warehouse", "wip_warehouse"], true);
frm.fields_dict.required_items.grid.toggle_reqd("source_warehouse", true);
frm.toggle_reqd("transfer_material_against", frm.doc.operations);
frm.toggle_reqd("transfer_material_against",
frm.doc.operations && frm.doc.operations.length > 0);
frm.fields_dict.operations.grid.toggle_reqd("workstation", frm.doc.operations);
},

View File

@ -1,5 +1,6 @@
execute:import unidecode # new requirement
erpnext.patches.v8_0.move_perpetual_inventory_setting
erpnext.patches.v8_9.set_print_zero_amount_taxes
erpnext.patches.v11_0.rename_production_order_to_work_order
erpnext.patches.v11_0.refactor_naming_series
erpnext.patches.v11_0.refactor_autoname_naming
@ -442,7 +443,6 @@ erpnext.patches.v8_9.add_setup_progress_actions #08-09-2017 #26-09-2017 #22-11-2
erpnext.patches.v8_9.rename_company_sales_target_field
erpnext.patches.v8_8.set_bom_rate_as_per_uom
erpnext.patches.v8_8.add_new_fields_in_accounts_settings
erpnext.patches.v8_9.set_print_zero_amount_taxes
erpnext.patches.v8_9.set_default_customer_group
erpnext.patches.v8_9.delete_gst_doctypes_for_outside_india_accounts
erpnext.patches.v8_9.set_default_fields_in_variant_settings
@ -572,7 +572,7 @@ execute:frappe.delete_doc_if_exists("Page", "sales-analytics")
execute:frappe.delete_doc_if_exists("Page", "purchase-analytics")
execute:frappe.delete_doc_if_exists("Page", "stock-analytics")
execute:frappe.delete_doc_if_exists("Page", "production-analytics")
erpnext.patches.v11_0.ewaybill_fields_gst_india #2018-11-13
erpnext.patches.v11_0.ewaybill_fields_gst_india #2018-11-13 #2019-01-09
erpnext.patches.v11_0.drop_column_max_days_allowed
erpnext.patches.v11_0.change_healthcare_desktop_icons
erpnext.patches.v10_0.update_user_image_in_employee
@ -580,9 +580,10 @@ erpnext.patches.v11_0.update_delivery_trip_status
erpnext.patches.v10_0.repost_gle_for_purchase_receipts_with_rejected_items
erpnext.patches.v11_0.set_missing_gst_hsn_code
erpnext.patches.v11_0.rename_bom_wo_fields
erpnext.patches.v11_0.rename_additional_salary_component_additional_salary
erpnext.patches.v11_0.renamed_from_to_fields_in_project
erpnext.patches.v12_0.change_qms_icon
erpnext.patches.v12_0.rename_learn_to_help
erpnext.patches.v12_0.rename_accounts_desktop_icon_to_accounting
erpnext.patches.v12_0.replace_project_list_desktop_icon_with_projects_module_desktop_icon
erpnext.patches.v12_0.move_item_tax_to_item_tax_template
erpnext.patches.v11_0.rename_additional_salary_component_additional_salary

View File

@ -0,0 +1,13 @@
# 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.model.utils.rename_field import rename_field
def execute():
frappe.reload_doc('projects', 'doctype', 'project')
if frappe.db.has_column('Project', 'from'):
rename_field('Project', 'from', 'from_time')
rename_field('Project', 'to', 'to_time')

File diff suppressed because it is too large Load Diff

View File

@ -3,16 +3,16 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import flt, getdate, get_url, now
from frappe import _
from frappe.model.document import Document
from six import iteritems
from email_reply_parser import EmailReplyParser
from frappe.utils import (flt, getdate, get_url, now,
nowtime, get_time, today, get_datetime, add_days)
from erpnext.controllers.queries import get_filters_cond
from frappe.desk.reportview import get_match_cond
import datetime
from six import iteritems
from erpnext.hr.doctype.daily_work_summary.daily_work_summary import get_users_email
from erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group import is_holiday_today
from frappe.model.document import Document
class Project(Document):
def get_feed(self):
@ -406,56 +406,137 @@ def get_users_for_project(doctype, txt, searchfield, start, page_len, filters):
def get_cost_center_name(project):
return frappe.db.get_value("Project", project, "cost_center")
@frappe.whitelist()
def hourly_reminder():
project = frappe.db.sql("""SELECT `tabProject`.name FROM `tabProject` WHERE `tabProject`.frequency = "Hourly" and (CURTIME() BETWEEN `tabProject`.from and `tabProject`.to) AND `tabProject`.collect_progress = 1 ORDER BY `tabProject`.name;""")
create_project_update(project)
fields = ["from_time", "to_time"]
projects = get_projects_for_collect_progress("Hourly", fields)
@frappe.whitelist()
def twice_daily_reminder():
project = frappe.db.sql("""SELECT `tabProject User`.user FROM `tabProject User` INNER JOIN `tabProject` ON `tabProject`.project_name = `tabProject User`.parent WHERE (`tabProject`.frequency = "Twice Daily") AND ((`tabProject`.first_email BETWEEN DATE_ADD(curtime(), INTERVAL -15 MINUTE) AND DATE_ADD(curtime(), INTERVAL 15 MINUTE)) OR (`tabProject`.second_email BETWEEN DATE_ADD(curtime(), INTERVAL -15 MINUTE) AND DATE_ADD(curtime(), INTERVAL 15 MINUTE))) AND `tabProject`.collect_progress = 1;""")
create_project_update(project)
for project in projects:
if (get_time(nowtime()) >= get_time(project.from_time) or
get_time(nowtime()) <= get_time(project.to_time)):
send_project_update_email_to_users(project.name)
def project_status_update_reminder():
daily_reminder()
twice_daily_reminder()
weekly_reminder()
@frappe.whitelist()
def daily_reminder():
project = frappe.db.sql("""SELECT `tabProject User`.user FROM `tabProject User` INNER JOIN `tabProject` ON `tabProject`.project_name = `tabProject User`.parent WHERE (`tabProject`.frequency = "Daily") AND (`tabProject`.daily_time_to_send BETWEEN DATE_ADD(curtime(), INTERVAL -15 MINUTE) AND DATE_ADD(curtime(), INTERVAL 15 MINUTE)) AND `tabProject`.collect_progress = 1;""")
create_project_update(project)
fields = ["daily_time_to_send"]
projects = get_projects_for_collect_progress("Daily", fields)
@frappe.whitelist()
def weekly():
today = datetime.datetime.now().strftime("%A")
project = frappe.db.sql("""SELECT `tabProject User`.user FROM `tabProject User` INNER JOIN `tabProject` ON `tabProject`.project_name = `tabProject User`.parent WHERE (`tabProject`.frequency = "Weekly") AND (`tabProject`.day_to_send = %s) AND (`tabProject`.weekly_time_to_send BETWEEN DATE_ADD(curtime(), INTERVAL -15 MINUTE) AND DATE_ADD(curtime(), INTERVAL 15 MINUTE)) AND `tabProject`.collect_progress = 1""", today)
create_project_update(project)
for project in projects:
if not check_project_update_exists(project.name, project.get("daily_time_to_send")):
send_project_update_email_to_users(project.name)
#Call this function in order to generate the Project Update for a specific project
def create_project_update(project):
data = []
date_today = datetime.date.today()
time_now = frappe.utils.now_datetime().strftime('%H:%M:%S')
for projects in project:
project_update_dict = {
"doctype" : "Project Update",
"project" : projects[0],
"date": date_today,
"time": time_now,
"naming_series": "UPDATE-.project.-.YY.MM.DD.-"
def twice_daily_reminder():
fields = ["first_email", "second_email"]
projects = get_projects_for_collect_progress("Twice Daily", fields)
for project in projects:
for d in fields:
if not check_project_update_exists(project.name, project.get(d)):
send_project_update_email_to_users(project.name)
def weekly_reminder():
fields = ["day_to_send", "weekly_time_to_send"]
projects = get_projects_for_collect_progress("Weekly", fields)
current_day = get_datetime().strftime("%A")
for project in projects:
if current_day != project.day_to_send:
continue
if not check_project_update_exists(project.name, project.get("weekly_time_to_send")):
send_project_update_email_to_users(project.name)
def check_project_update_exists(project, time):
data = frappe.db.sql(""" SELECT name from `tabProject Update`
WHERE project = %s and date = %s and time >= %s """, (project, today(), time))
return True if data and data[0][0] else False
def get_projects_for_collect_progress(frequency, fields):
fields.extend(["name"])
return frappe.get_all("Project", fields = fields,
filters = {'collect_progress': 1, 'frequency': frequency})
def send_project_update_email_to_users(project):
doc = frappe.get_doc('Project', project)
if is_holiday_today(doc.holiday_list) or not doc.users: return
project_update = frappe.get_doc({
"doctype" : "Project Update",
"project" : project,
"sent": 0,
"date": today(),
"time": nowtime(),
"naming_series": "UPDATE-.project.-.YY.MM.DD.-",
}).insert()
subject = "For project %s, update your status" % (project)
incoming_email_account = frappe.db.get_value('Email Account',
dict(enable_incoming=1, default_incoming=1), 'email_id')
frappe.sendmail(recipients=get_users_email(doc),
message=doc.message,
subject=_(subject),
reference_doctype=project_update.doctype,
reference_name=project_update.name,
reply_to=incoming_email_account
)
def collect_project_status():
for data in frappe.get_all("Project Update",
{'date': today(), 'sent': 0}):
replies = frappe.get_all('Communication',
fields=['content', 'text_content', 'sender'],
filters=dict(reference_doctype="Project Update",
reference_name=data.name,
communication_type='Communication',
sent_or_received='Received'),
order_by='creation asc')
for d in replies:
doc = frappe.get_doc("Project Update", data.name)
user_data = frappe.db.get_values("User", {"email": d.sender},
["full_name", "user_image", "name"], as_dict=True)[0]
doc.append("users", {
'user': user_data.name,
'full_name': user_data.full_name,
'image': user_data.user_image,
'project_status': frappe.utils.md_to_html(
EmailReplyParser.parse_reply(d.text_content) or d.content
)
})
doc.save(ignore_permissions=True)
def send_project_status_email_to_users():
yesterday = add_days(today(), -1)
for d in frappe.get_all("Project Update",
{'date': yesterday, 'sent': 0}):
doc = frappe.get_doc("Project Update", d.name)
project_doc = frappe.get_doc('Project', doc.project)
args = {
"users": doc.users,
"title": _("Project Summary for {0}").format(yesterday)
}
project_update = frappe.get_doc(project_update_dict)
project_update.insert()
#you can edit your local_host
local_host = "http://localhost:8003"
project_update_url = "<a class = 'btn btn-primary' href=%s target='_blank'>" % (local_host +"/desk#Form/Project%20Update/" + (project_update.name)) + ("CREATE PROJECT UPDATE" + "</a>")
data.append(project_update_url)
email = frappe.db.sql("""SELECT user from `tabProject User` WHERE parent = %s;""", project[0])
for emails in email:
frappe.sendmail(
recipients=emails,
subject=frappe._(projects[0]),
header=[frappe._("Please Update your Project Status"), 'blue'],
message= project_update_url
)
return data
frappe.sendmail(recipients=get_users_email(project_doc),
template='daily_project_summary',
args=args,
subject=_("Daily Project Summary for {0}").format(d.name),
reference_doctype="Project Update",
reference_name=d.name)
doc.db_set('sent', 1)
def update_project_sales_billing():
sales_update_frequency = frappe.db.get_single_value("Selling Settings", "sales_update_frequency")

View File

@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@ -13,6 +14,40 @@
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fieldname": "naming_series",
"fieldtype": "Data",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Series",
"length": 0,
"no_copy": 0,
"options": "PROJ-UPD-.YYYY.-",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@ -46,6 +81,39 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0",
"fieldname": "sent",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Sent",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@ -172,39 +240,6 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "progress",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "How is the Project Progressing Right Now?",
"length": 0,
"no_copy": 0,
"options": "Not Updated\nGreat/Quickly\nGood/Steady\nChallenging/Slow\nProblematic/Stuck",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@ -238,38 +273,6 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "progress_details",
"fieldtype": "Text Editor",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Progress Details",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@ -301,40 +304,6 @@
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fieldname": "naming_series",
"fieldtype": "Data",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Series",
"length": 0,
"no_copy": 0,
"options": "PROJ-UPD-.YYYY.-",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
@ -347,7 +316,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-08-21 14:44:21.287709",
"modified": "2019-01-16 19:31:05.210656",
"modified_by": "Administrator",
"module": "Projects",
"name": "Project Update",

View File

@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@ -44,6 +45,136 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "user.email",
"fieldname": "email",
"fieldtype": "Read Only",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Email",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "user.user_image",
"fieldname": "image",
"fieldtype": "Read Only",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Image",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_2",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "user.full_name",
"fieldname": "full_name",
"fieldtype": "Read Only",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Full Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@ -107,6 +238,70 @@
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_5",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:parent.doctype == 'Project Update'",
"fieldname": "project_status",
"fieldtype": "Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Project Status",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
@ -119,7 +314,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-09-09 12:39:38.376816",
"modified": "2019-01-17 17:10:05.339735",
"modified_by": "Administrator",
"module": "Projects",
"name": "Project User",

View File

@ -28,7 +28,10 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
if (this.frm.doc.__islocal
&& frappe.meta.has_field(this.frm.doc.doctype, "disable_rounded_total")) {
this.frm.set_value("disable_rounded_total", cint(frappe.sys_defaults.disable_rounded_total));
var df = frappe.meta.get_docfield(this.frm.doc.doctype, "disable_rounded_total");
var disable = df.default || cint(frappe.sys_defaults.disable_rounded_total);
this.frm.set_value("disable_rounded_total", disable);
}
/* eslint-disable */
@ -127,7 +130,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
if (doc.doctype == "Purchase Order" && item.blanket_order_rate) {
item_rate = item.blanket_order_rate;
}
item.discount_amount = flt(item_rate) * flt(item.discount_percentage) / 100;
item.discount_amount = flt(item_rate) * flt(item.discount_percentage) / 100;
item.rate = flt((item.price_list_rate) - (item.discount_amount), precision('rate', item));
this.calculate_taxes_and_totals();
@ -274,26 +277,26 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
d.qty = d.qty - my_qty;
cur_frm.doc.items[i].stock_qty = my_qty*cur_frm.doc.items[i].conversion_factor;
cur_frm.doc.items[i].qty = my_qty;
frappe.msgprint("Assigning " + d.mr_name + " to " + d.item_code + " (row " + cur_frm.doc.items[i].idx + ")");
if (qty > 0)
{
frappe.msgprint("Splitting " + qty + " units of " + d.item_code);
var newrow = frappe.model.add_child(cur_frm.doc, cur_frm.doc.items[i].doctype, "items");
item_length++;
for (var key in cur_frm.doc.items[i])
{
newrow[key] = cur_frm.doc.items[i][key];
}
newrow.idx = item_length;
newrow["stock_qty"] = newrow.conversion_factor*qty;
newrow["qty"] = qty;
newrow["material_request"] = "";
newrow["material_request_item"] = "";
}
}
});
@ -310,7 +313,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
if (doc.auto_repeat) {
frappe.call({
method:"frappe.desk.doctype.auto_repeat.auto_repeat.update_reference",
args:{
args:{
docname: doc.auto_repeat,
reference:doc.name
},
@ -435,4 +438,3 @@ erpnext.buying.get_items_from_product_bundle = function(frm) {
});
dialog.show();
}

View File

@ -7,5 +7,5 @@ from erpnext import get_region
def check_deletion_permission(doc, method):
region = get_region(doc.company)
if region in ["Nepal", "France"]:
if region in ["Nepal", "France"] and doc.docstatus != 0:
frappe.throw(_("Deletion is not permitted for country {0}".format(region)))

View File

@ -126,6 +126,9 @@ def make_custom_fields(update=True):
dict(fieldname='place_of_supply', label='Place of Supply',
fieldtype='Data', insert_after='shipping_address',
print_hide=1, read_only=0),
]
purchase_invoice_itc_fields = [
dict(fieldname='eligibility_for_itc', label='Eligibility For ITC',
fieldtype='Select', insert_after='reason_for_issuing_document', print_hide=1,
options='input\ninput service\ncapital goods\nineligible', default="ineligible"),
@ -152,6 +155,9 @@ def make_custom_fields(update=True):
dict(fieldname='company_gstin', label='Company GSTIN',
fieldtype='Data', insert_after='company_address',
fetch_from='company_address.gstin', print_hide=1),
]
sales_invoice_shipping_fields = [
dict(fieldname='port_code', label='Port Code',
fieldtype='Data', insert_after='reason_for_issuing_document', print_hide=1,
depends_on="eval:doc.invoice_type=='Export' "),
@ -214,9 +220,12 @@ def make_custom_fields(update=True):
dict(fieldname='gst_state_number', label='GST State Number',
fieldtype='Data', insert_after='gst_state', read_only=1),
],
'Purchase Invoice': invoice_gst_fields + purchase_invoice_gst_fields,
'Sales Invoice': invoice_gst_fields + sales_invoice_gst_fields,
'Delivery Note': sales_invoice_gst_fields + ewaybill_fields,
'Purchase Invoice': invoice_gst_fields + purchase_invoice_gst_fields + purchase_invoice_itc_fields,
'Purchase Order': purchase_invoice_gst_fields,
'Purchase Receipt': purchase_invoice_gst_fields,
'Sales Invoice': invoice_gst_fields + sales_invoice_gst_fields + sales_invoice_shipping_fields,
'Delivery Note': sales_invoice_gst_fields + ewaybill_fields + sales_invoice_shipping_fields,
'Sales Order': sales_invoice_gst_fields,
'Sales Taxes and Charges Template': inter_state_gst_field,
'Purchase Taxes and Charges Template': inter_state_gst_field,
'Item': [

View File

@ -370,7 +370,7 @@
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"in_standard_filter": 1,
"label": "Date",
"length": 0,
"no_copy": 1,

View File

@ -31,6 +31,26 @@ class TestQuotation(unittest.TestCase):
self.assertFalse(sales_order.get('payment_schedule'))
def test_make_sales_order_with_different_currency(self):
from erpnext.selling.doctype.quotation.quotation import make_sales_order
quotation = frappe.copy_doc(test_records[0])
quotation.transaction_date = nowdate()
quotation.valid_till = add_months(quotation.transaction_date, 1)
quotation.insert()
quotation.submit()
sales_order = make_sales_order(quotation.name)
sales_order.currency = "USD"
sales_order.conversion_rate = 20.0
sales_order.delivery_date = "2019-01-01"
sales_order.naming_series = "_T-Quotation-"
sales_order.transaction_date = nowdate()
sales_order.insert()
self.assertEquals(sales_order.currency, "USD")
self.assertNotEqual(sales_order.currency, quotation.currency)
def test_make_sales_order(self):
from erpnext.selling.doctype.quotation.quotation import make_sales_order

View File

@ -17,6 +17,20 @@ frappe.ui.form.on("Sales Order", {
// formatter for material request item
frm.set_indicator_formatter('item_code',
function(doc) { return (doc.stock_qty<=doc.delivered_qty) ? "green" : "orange" })
frm.set_query('company_address', function(doc) {
if(!doc.company) {
frappe.throw(__('Please set Company'));
}
return {
query: 'frappe.contacts.doctype.address.address.address_query',
filters: {
link_doctype: 'Company',
link_name: doc.company
}
};
})
},
refresh: function(frm) {
if(frm.doc.docstatus == 1 && frm.doc.status == 'To Deliver and Bill') {

View File

@ -368,7 +368,7 @@
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"in_standard_filter": 1,
"label": "Date",
"length": 0,
"no_copy": 1,
@ -747,6 +747,71 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company_address_display",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company_address",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Company Address",
"length": 0,
"no_copy": 0,
"options": "Address",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,

View File

@ -147,7 +147,7 @@ class SalesOrder(SellingController):
super(SalesOrder, self).validate_with_previous_doc({
"Quotation": {
"ref_dn_field": "prevdoc_docname",
"compare_fields": [["company", "="], ["currency", "="]]
"compare_fields": [["company", "="]]
}
})

View File

@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
@ -352,7 +353,7 @@
"fieldname": "to_emp",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
@ -618,7 +619,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-08-21 16:15:42.627233",
"modified": "2019-01-21 17:10:39.822125",
"modified_by": "Administrator",
"module": "Setup",
"name": "Authorization Rule",

View File

@ -1,5 +1,7 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "Prompt",
@ -13,6 +15,8 @@
"editable_grid": 0,
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -23,6 +27,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Email Digest Settings",
@ -37,9 +42,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -50,6 +58,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@ -63,9 +72,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -76,6 +88,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Enabled",
@ -90,9 +103,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -103,6 +119,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "For Company",
@ -118,9 +135,12 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -131,6 +151,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "How frequently?",
@ -146,9 +167,12 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -160,6 +184,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Next email will be sent on:",
@ -174,9 +199,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -187,6 +215,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@ -200,9 +229,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -214,6 +246,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Recipients",
@ -229,9 +262,12 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -242,6 +278,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Add/Remove Recipients",
@ -256,9 +293,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -269,6 +309,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Accounts",
@ -283,9 +324,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -296,6 +340,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Profit & Loss",
@ -310,9 +355,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -324,6 +372,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "New Income",
@ -338,9 +387,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -352,6 +404,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "New Expenses",
@ -366,9 +419,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -379,6 +435,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Annual Income",
@ -393,9 +450,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -406,6 +466,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Annual Expenses",
@ -421,9 +482,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -435,6 +499,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Balance Sheet",
@ -449,9 +514,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -463,6 +531,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Bank Balance",
@ -477,9 +546,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -490,9 +562,10 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Credit Balance",
"label": "Bank Credit Balance",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -505,9 +578,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -519,6 +595,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Receivables",
@ -533,9 +610,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -547,6 +627,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Payables",
@ -561,9 +642,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -574,8 +658,9 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"in_standard_filter": 0,
"label": "Work in Progress",
"length": 0,
"no_copy": 0,
@ -589,9 +674,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -602,8 +690,9 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"in_standard_filter": 0,
"label": "Sales Orders to Bill",
"length": 0,
"no_copy": 0,
@ -617,9 +706,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -630,8 +722,9 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"in_standard_filter": 0,
"label": "Purchase Orders to Bill",
"length": 0,
"no_copy": 0,
@ -645,9 +738,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -658,6 +754,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Operations",
@ -673,9 +770,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -686,6 +786,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
@ -700,9 +801,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -713,6 +817,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "New Sales Orders",
@ -728,9 +833,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -741,8 +849,9 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"in_standard_filter": 0,
"label": "New Purchase Orders",
"length": 0,
"no_copy": 0,
@ -756,9 +865,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -769,8 +881,9 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"in_standard_filter": 0,
"label": "Sales Orders to Deliver",
"length": 0,
"no_copy": 0,
@ -784,9 +897,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -797,8 +913,9 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"in_standard_filter": 0,
"label": "Purchase Orders to Receive",
"length": 0,
"no_copy": 0,
@ -812,9 +929,76 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "sales_invoice",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "New Sales Invoice",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "purchase_invoice",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "New Purchase Invoice",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -825,6 +1009,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
@ -840,9 +1025,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -853,6 +1041,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "New Quotations",
@ -868,9 +1057,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -881,8 +1073,9 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"in_standard_filter": 0,
"label": "Open Quotations",
"length": 0,
"no_copy": 0,
@ -896,9 +1089,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -909,6 +1105,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Open Issues",
@ -924,9 +1121,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -937,6 +1137,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Open Projects",
@ -952,9 +1153,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -965,8 +1169,9 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"in_standard_filter": 0,
"label": "Purchase Orders Items Overdue",
"length": 0,
"no_copy": 0,
@ -980,9 +1185,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -993,6 +1201,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Other",
@ -1008,9 +1217,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -1021,6 +1233,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Tools",
@ -1036,9 +1249,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -1049,6 +1265,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Upcoming Calendar Events",
@ -1064,9 +1281,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -1077,6 +1297,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Open To Do",
@ -1092,9 +1313,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -1105,6 +1329,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Open Notifications",
@ -1120,9 +1345,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -1133,6 +1361,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": " ",
@ -1148,9 +1377,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -1161,6 +1393,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Add Quote",
@ -1176,22 +1409,23 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-envelope",
"idx": 1,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2018-09-16 22:00:00.000000",
"modified": "2019-01-16 09:52:15.149908",
"modified_by": "Administrator",
"module": "Setup",
"name": "Email Digest",
@ -1199,7 +1433,6 @@
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
@ -1207,7 +1440,6 @@
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@ -1220,7 +1452,6 @@
},
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 0,
"delete": 0,
@ -1228,7 +1459,6 @@
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 1,
"print": 0,
"read": 1,
@ -1243,7 +1473,10 @@
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_seen": 0
"track_changes": 0,
"track_seen": 0,
"track_views": 0
}

View File

@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import fmt_money, formatdate, format_time, now_datetime, \
get_url_to_form, get_url_to_list, flt, getdate
get_url_to_form, get_url_to_list, flt, get_link_to_report
from datetime import timedelta
from dateutil.relativedelta import relativedelta
from frappe.core.doctype.user.user import STANDARD_USERS
@ -239,7 +239,9 @@ class EmailDigest(Document):
for key in ("income", "expenses_booked", "income_year_to_date", "expense_year_to_date",
"bank_balance", "credit_balance", "invoiced_amount", "payables",
"sales_orders_to_bill", "purchase_orders_to_bill", "sales_order", "purchase_order",
"sales_orders_to_deliver", "purchase_orders_to_receive", "new_quotations", "pending_quotations"):
"sales_orders_to_deliver", "purchase_orders_to_receive", "sales_invoice", "purchase_invoice",
"new_quotations", "pending_quotations"):
if self.get(key):
cache_key = "email_digest:card:{0}:{1}:{2}:{3}".format(self.company, self.frequency, key, self.from_date)
card = cache.get(cache_key)
@ -288,8 +290,24 @@ class EmailDigest(Document):
"""Get income for given period"""
income, past_income, count = self.get_period_amounts(self.get_roots("income"),'income')
income_account = frappe.db.get_all('Account',
fields=["name"],
filters={
"root_type":"Income",
"parent_account":'',
"company": self.company
})
label = get_link_to_report("General Ledger",self.meta.get_label("income"),
filters={
"from_date": self.future_from_date,
"to_date": self.future_to_date,
"account": income_account[0].name,
"company": self.company
}
)
return {
"label": self.meta.get_label("income"),
"label": label,
"value": income,
"last_value": past_income,
"count": count
@ -312,8 +330,20 @@ class EmailDigest(Document):
balance += get_balance_on(account, date = self.future_to_date)
count += get_count_on(account, fieldname, date = self.future_to_date)
if fieldname == 'income':
filters = {
"currency": self.currency
}
label = get_link_to_report('Profit and Loss Statement', label=self.meta.get_label(root_type + "_year_to_date"), filters=filters)
elif fieldname == 'expenses_booked':
filters = {
"currency": self.currency
}
label = get_link_to_report('Profit and Loss Statement', label=self.meta.get_label(root_type + "_year_to_date"), filters=filters)
return {
"label": self.meta.get_label(root_type + "_year_to_date"),
"label": label,
"value": balance,
"count": count
}
@ -335,8 +365,25 @@ class EmailDigest(Document):
def get_expenses_booked(self):
expenses, past_expenses, count = self.get_period_amounts(self.get_roots("expense"), 'expenses_booked')
expense_account = frappe.db.get_all('Account',
fields=["name"],
filters={
"root_type": "Expense",
"parent_account": '',
"company": self.company
}
)
label = get_link_to_report("General Ledger",self.meta.get_label("expenses_booked"),
filters={
"company":self.company,
"from_date":self.future_from_date,
"to_date":self.future_to_date,
"account": expense_account[0].name
}
)
return {
"label": self.meta.get_label("expenses_booked"),
"label": label,
"value": expenses,
"last_value": past_expenses,
"count": count
@ -361,8 +408,19 @@ class EmailDigest(Document):
where (transaction_date <= %(to_date)s) and billing_status != "Fully Billed"
and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date})[0]
label = get_link_to_report('Sales Order', label=self.meta.get_label("sales_orders_to_bill"),
report_type="Report Builder",
doctype="Sales Order",
filters = {
"status": [['!=', "Closed"], ['!=', "Cancelled"]],
"per_billed": [['<', 100]],
"transaction_date": [['<=', self.future_to_date]],
"company": self.company
}
)
return {
"label": self.meta.get_label("sales_orders_to_bill"),
"label": label,
"value": value,
"count": count
}
@ -370,13 +428,24 @@ class EmailDigest(Document):
def get_sales_orders_to_deliver(self):
"""Get value not delivered"""
value, count = frappe.db.sql("""select ifnull((sum(grand_total)) - (sum(grand_total*per_delivered/100)),0),
value, count = frappe.db.sql("""select ifnull((sum(grand_total)) - (sum(grand_total*per_delivered/100)),0),
count(*) from `tabSales Order`
where (transaction_date <= %(to_date)s) and delivery_status != "Fully Delivered"
and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date})[0]
label = get_link_to_report('Sales Order', label=self.meta.get_label("sales_orders_to_deliver"),
report_type="Report Builder",
doctype="Sales Order",
filters = {
"status": [['!=', "Closed"], ['!=', "Cancelled"], ['!=', "Completed"]],
"delivery_status": [['!=', "Fully Delivered"]],
"transaction_date": [['<=', self.future_to_date]],
"company": self.company
}
)
return {
"label": self.meta.get_label("sales_orders_to_deliver"),
"label": label,
"value": value,
"count": count
}
@ -389,8 +458,19 @@ class EmailDigest(Document):
where (transaction_date <= %(to_date)s) and per_received < 100
and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date})[0]
label = get_link_to_report('Purchase Order', label=self.meta.get_label("purchase_orders_to_receive"),
report_type="Report Builder",
doctype="Purchase Order",
filters = {
"status": [['!=', "Closed"], ['!=', "Cancelled"], ['!=', "Completed"]],
"per_received": [['<', 100]],
"transaction_date": [['<=', self.future_to_date]],
"company": self.company
}
)
return {
"label": self.meta.get_label("purchase_orders_to_receive"),
"label": label,
"value": value,
"count": count
}
@ -403,8 +483,19 @@ class EmailDigest(Document):
where (transaction_date <= %(to_date)s) and per_billed < 100
and status not in ('Closed','Cancelled', 'Completed') """, {"to_date": self.future_to_date})[0]
label = get_link_to_report('Purchase Order', label=self.meta.get_label("purchase_orders_to_bill"),
report_type="Report Builder",
doctype="Purchase Order",
filters = {
"status": [['!=', "Closed"], ['!=', "Cancelled"], ['!=', "Completed"]],
"per_received": [['<', 100]],
"transaction_date": [['<=', self.future_to_date]],
"company": self.company
}
)
return {
"label": self.meta.get_label("purchase_orders_to_bill"),
"label": label,
"value": value,
"count": count
}
@ -428,13 +519,47 @@ class EmailDigest(Document):
prev_balance += get_balance_on(account, date=self.past_to_date, in_account_currency=False)
if fieldname in ("bank_balance","credit_balance"):
label = ""
if fieldname == "bank_balance":
filters = {
"root_type": "Asset",
"account_type": "Bank",
"date": self.future_to_date,
"company": self.company
}
label = get_link_to_report('Account Balance', label=self.meta.get_label(fieldname), filters=filters)
else:
filters = {
"root_type": "Liability",
"account_type": "Bank",
"to_date": self.future_to_date,
"company": self.company
}
label = get_link_to_report('Account Balance', label=self.meta.get_label(fieldname), filters=filters)
return {
'label': self.meta.get_label(fieldname),
'label': label,
'value': balance,
'last_value': prev_balance }
'last_value': prev_balance
}
else:
if account_type == 'Payable':
label = get_link_to_report('Accounts Payable', label=self.meta.get_label(fieldname),
filters={
"report_date": self.future_to_date,
"company": self.company
} )
elif account_type == 'Receivable':
label = get_link_to_report('Accounts Receivable', label=self.meta.get_label(fieldname),
filters={
"report_date": self.future_to_date,
"company": self.company
})
else:
label = self.meta.get_label(fieldname)
return {
'label': self.meta.get_label(fieldname),
'label': label,
'value': balance,
'last_value': prev_balance,
'count': count
@ -468,6 +593,14 @@ class EmailDigest(Document):
return self.get_summary_of_pending("Sales Order","pending_sales_orders","per_delivered")
def get_sales_invoice(self):
return self.get_summary_of_doc("Sales Invoice","sales_invoice")
def get_purchase_invoice(self):
return self.get_summary_of_doc("Purchase Invoice","purchase_invoice")
def get_new_quotations(self):
return self.get_summary_of_doc("Quotation","new_quotations")
@ -487,10 +620,10 @@ class EmailDigest(Document):
return {
"label": self.meta.get_label(fieldname),
"value": value,
"value": value,
"billed_value": billed_value,
"delivered_value": delivered_value,
"count": count
"count": count
}
def get_summary_of_pending_quotations(self, fieldname):
@ -505,33 +638,62 @@ class EmailDigest(Document):
and company = %(company)s
and status not in ('Ordered','Cancelled', 'Lost') """,{"to_date": self.past_to_date, "company": self.company})[0][0]
label = get_link_to_report('Quotation', label=self.meta.get_label(fieldname),
report_type="Report Builder",
doctype="Quotation",
filters = {
"status": [['!=', "Ordered"], ['!=', "Cancelled"], ['!=', "Lost"]],
"per_received": [['<', 100]],
"transaction_date": [['<=', self.future_to_date]],
"company": self.company
}
)
return {
"label": self.meta.get_label(fieldname),
"value": value,
"label": label,
"value": value,
"last_value": last_value,
"count": count
"count": count
}
def get_summary_of_doc(self, doc_type, fieldname):
value = self.get_total_on(doc_type, self.future_from_date, self.future_to_date)[0]
count = self.get_total_on(doc_type, self.future_from_date, self.future_to_date)[1]
date_field = 'posting_date' if doc_type in ['Sales Invoice', 'Purchase Invoice'] \
else 'transaction_date'
last_value =self.get_total_on(doc_type, self.past_from_date, self.past_to_date)[0]
value = flt(self.get_total_on(doc_type, self.future_from_date, self.future_to_date)[0].grand_total)
count = self.get_total_on(doc_type, self.future_from_date, self.future_to_date)[0].count
last_value = flt(self.get_total_on(doc_type, self.past_from_date, self.past_to_date)[0].grand_total)
filters = {
date_field: [['>=', self.future_from_date], ['<=', self.future_to_date]],
"status": [['!=','Cancelled']],
"company": self.company
}
label = get_link_to_report(doc_type,label=self.meta.get_label(fieldname),
report_type="Report Builder", filters=filters, doctype=doc_type)
return {
"label": self.meta.get_label(fieldname),
"value": value,
"last_value": last_value,
"label": label,
"value": value,
"last_value": last_value,
"count": count
}
def get_total_on(self, doc_type, from_date, to_date):
return frappe.db.sql("""select ifnull(sum(grand_total),0), count(*) from `tab{0}`
where (transaction_date between %(from_date)s and %(to_date)s) and company=%(company)s
and status not in ('Cancelled')""".format(doc_type),
{"from_date": from_date, "to_date": to_date, "company": self.company})[0]
date_field = 'posting_date' if doc_type in ['Sales Invoice', 'Purchase Invoice'] \
else 'transaction_date'
return frappe.get_all(doc_type,
filters={
date_field: ['between', (from_date, to_date)],
'status': ['not in', ('Cancelled')],
'company': self.company
},
fields=['count(*) as count', 'sum(grand_total) as grand_total'])
def get_from_to_date(self):
today = now_datetime().date()
@ -597,13 +759,13 @@ class EmailDigest(Document):
fields_poi = "`tabPurchase Order Item`.parent, `tabPurchase Order Item`.schedule_date, item_code," \
"received_qty, qty - received_qty as missing_qty, rate, amount"
sql_po = """select {fields} from `tabPurchase Order Item`
sql_po = """select {fields} from `tabPurchase Order Item`
left join `tabPurchase Order` on `tabPurchase Order`.name = `tabPurchase Order Item`.parent
where status<>'Closed' and `tabPurchase Order Item`.docstatus=1 and curdate() > `tabPurchase Order Item`.schedule_date
and received_qty < qty order by `tabPurchase Order Item`.parent DESC,
`tabPurchase Order Item`.schedule_date DESC""".format(fields=fields_po)
sql_poi = """select {fields} from `tabPurchase Order Item`
sql_poi = """select {fields} from `tabPurchase Order Item`
left join `tabPurchase Order` on `tabPurchase Order`.name = `tabPurchase Order Item`.parent
where status<>'Closed' and `tabPurchase Order Item`.docstatus=1 and curdate() > `tabPurchase Order Item`.schedule_date
and received_qty < qty order by `tabPurchase Order Item`.idx""".format(fields=fields_poi)
@ -631,11 +793,11 @@ def get_digest_msg(name):
def get_incomes_expenses_for_period(account, from_date, to_date):
"""Get amounts for current and past periods"""
val = 0.0
balance_on_to_date = get_balance_on(account, date = to_date)
balance_before_from_date = get_balance_on(account, date = from_date - timedelta(days=1))
fy_start_date = get_fiscal_year(to_date)[1]
if from_date == fy_start_date:

View File

@ -150,7 +150,7 @@ def adjust_qty_for_expired_items(data):
if item.get('has_batch_no') and item.get('website_warehouse'):
stock_qty_dict = get_qty_in_stock(
item.get('name'), 'website_warehouse', item.get('website_warehouse'))
qty = stock_qty_dict.stock_qty[0][0]
qty = stock_qty_dict.stock_qty[0][0] if stock_qty_dict.stock_qty else 0
item['in_stock'] = 1 if qty else 0
adjusted_data.append(item)

View File

@ -66,7 +66,7 @@ def place_order():
sales_order = frappe.get_doc(_make_sales_order(quotation.name, ignore_permissions=True))
for item in sales_order.get("items"):
item.reserved_warehouse, is_stock_item = frappe.db.get_value("Item",
item.item_code, ["website_warehouse", "is_stock_item"]) or None, None
item.item_code, ["website_warehouse", "is_stock_item"])
if is_stock_item:
item_stock = get_qty_in_stock(item.item_code, "website_warehouse")

View File

@ -5,6 +5,7 @@ $.extend(cur_frm.cscript, {
onload: function() {
if(cur_frm.doc.__onload && cur_frm.doc.__onload.quotation_series) {
cur_frm.fields_dict.quotation_series.df.options = cur_frm.doc.__onload.quotation_series;
cur_frm.refresh_field("quotation_series");
}
},
refresh: function(){

View File

@ -6,7 +6,7 @@ import unittest
from frappe.utils import nowdate
from erpnext.stock.doctype.item.test_item import create_item
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
from erpnext.controllers.stock_controller import QualityInspectionRejectedError, QualityInspectionRequiredError
from erpnext.controllers.stock_controller import QualityInspectionRejectedError, QualityInspectionRequiredError, QualityInspectionNotSubmittedError
# test_records = frappe.get_test_records('Quality Inspection')
@ -19,7 +19,7 @@ class TestQualityInspection(unittest.TestCase):
dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True)
self.assertRaises(QualityInspectionRequiredError, dn.submit)
qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, status="Rejected")
qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, status="Rejected", submit=True)
dn.reload()
self.assertRaises(QualityInspectionRejectedError, dn.submit)
@ -27,6 +27,12 @@ class TestQualityInspection(unittest.TestCase):
dn.reload()
dn.submit()
def test_qa_not_submit(self):
dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True)
qa = create_quality_inspection(reference_type="Delivery Note", reference_name=dn.name, submit = False)
dn.items[0].quality_inspection = qa.name
self.assertRaises(QualityInspectionNotSubmittedError, dn.submit)
def create_quality_inspection(**args):
args = frappe._dict(args)
qa = frappe.new_doc("Quality Inspection")
@ -42,6 +48,7 @@ def create_quality_inspection(**args):
"status": args.status
})
qa.save()
qa.submit()
if args.submit:
qa.submit()
return qa

View File

@ -302,6 +302,9 @@ def has_duplicate_serial_no(sn, sle):
if sn.warehouse:
return True
if sn.company != sle.company:
return False
status = False
if sn.purchase_document_no:
if sle.voucher_type in ['Purchase Receipt', 'Stock Entry', "Purchase Invoice"] and \
@ -357,6 +360,7 @@ def auto_make_serial_nos(args):
sr.warehouse = args.get('warehouse') if args.get('actual_qty', 0) > 0 else None
sr.batch_no = args.get('batch_no')
sr.location = args.get('location')
sr.company = args.get('company')
if sr.sales_order and args.get('voucher_type') == "Stock Entry" \
and not args.get('actual_qty', 0) > 0:
sr.sales_order = None

View File

@ -7,6 +7,12 @@
from __future__ import unicode_literals
import frappe, unittest
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
test_dependencies = ["Item"]
test_records = frappe.get_test_records('Serial No')
@ -29,3 +35,21 @@ class TestSerialNo(unittest.TestCase):
sr.warehouse = "_Test Warehouse - _TC"
self.assertTrue(SerialNoCannotCannotChangeError, sr.save)
def test_inter_company_transfer(self):
se = make_serialized_item(target_warehouse="_Test Warehouse - _TC")
serial_nos = get_serial_nos(se.get("items")[0].serial_no)
create_delivery_note(item_code="_Test Serialized Item With Series", qty=1, serial_no=serial_nos[0])
wh = create_warehouse("_Test Warehouse", company="_Test Company 1")
make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=1, serial_no=serial_nos[0],
company="_Test Company 1", warehouse=wh)
serial_no = frappe.db.get_value("Serial No", serial_nos[0], ["warehouse", "company"], as_dict=1)
self.assertEqual(serial_no.warehouse, wh)
self.assertEqual(serial_no.company, "_Test Company 1")
def tearDown(self):
frappe.db.rollback()

View File

@ -8,6 +8,7 @@ from erpnext import set_perpetual_inventory
from frappe.test_runner import make_test_records
from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account
import erpnext
import frappe
import unittest
test_records = frappe.get_test_records('Warehouse')
@ -90,17 +91,24 @@ class TestWarehouse(unittest.TestCase):
self.assertTrue(frappe.db.get_value("Warehouse",
filters={"account": "Test Warehouse for Merging 2 - _TC"}))
def create_warehouse(warehouse_name, properties=None):
if not frappe.db.exists("Warehouse", warehouse_name + " - _TC"):
def create_warehouse(warehouse_name, properties=None, company=None):
if not company:
company = "_Test Company"
warehouse_id = erpnext.encode_company_abbr(warehouse_name, company)
if not frappe.db.exists("Warehouse", warehouse_id):
w = frappe.new_doc("Warehouse")
w.warehouse_name = warehouse_name
w.parent_warehouse = "_Test Warehouse Group - _TC"
w.company = "_Test Company"
w.company = company
make_account_for_warehouse(warehouse_name, w)
w.account = warehouse_name + " - _TC"
w.account = warehouse_id
if properties:
w.update(properties)
w.save()
return w.name
else:
return warehouse_id
def make_account_for_warehouse(warehouse_name, warehouse_obj):
if not frappe.db.exists("Account", warehouse_name + " - _TC"):

View File

@ -0,0 +1,46 @@
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<h3>{{ title }}</h3>
</tr>
</table>
{% for user in users %}
<table class="panel-header" border="0" cellpadding="0" cellspacing="0" width="100%">
<tr height="10"></tr>
<tr>
<td width="15"></td>
<td valign="top" width="24">
{% if user.image %}
<img class="sender-avatar" width="24" height="24" embed="{{ user.image }}"/>
{% else %}
<div class="sender-avatar-placeholder">
{{ user.full_name[0] }}
</div>
{% endif %}
</td>
<td width="10"></td>
<td>
<div class="text-medium text-muted">
<span>{{ user.full_name }}</span>
</div>
</td>
<td width="15"></td>
</tr>
<tr height="10"></tr>
</table>
<table class="panel-body" border="0" cellpadding="0" cellspacing="0" width="100%">
<tr height="10"></tr>
<tr>
<td width="15"></td>
<td>
<div>
{{ user.project_status }}
</div>
</td>
<td width="15"></td>
</tr>
<tr height="10"></tr>
</table>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr height="20"></tr>
</table>
{% endfor %}