Merge branch 'staging-fixes' into permission-fixes
This commit is contained in:
commit
7ae2d59ffd
2
.github/stale.yml
vendored
2
.github/stale.yml
vendored
@ -1,7 +1,7 @@
|
||||
# Configuration for probot-stale - https://github.com/probot/stale
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
||||
daysUntilStale: 10
|
||||
daysUntilStale: 30
|
||||
|
||||
# Number of days of inactivity before a stale Issue or Pull Request is closed.
|
||||
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
|
||||
|
@ -33,7 +33,7 @@ before_script:
|
||||
- cd ~/frappe-bench
|
||||
- bench get-app erpnext $TRAVIS_BUILD_DIR
|
||||
- bench use test_site
|
||||
- bench reinstall --yes
|
||||
- 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
|
||||
|
@ -5,7 +5,7 @@ import frappe
|
||||
from erpnext.hooks import regional_overrides
|
||||
from frappe.utils import getdate
|
||||
|
||||
__version__ = '10.1.72'
|
||||
__version__ = '10.1.74'
|
||||
|
||||
def get_default_company(user=None):
|
||||
'''Get default company for user'''
|
||||
|
@ -70,12 +70,14 @@ class PeriodClosingVoucher(AccountsController):
|
||||
net_pl_balance += flt(acc.balance_in_company_currency)
|
||||
|
||||
if net_pl_balance:
|
||||
cost_center = frappe.db.get_value("Company", self.company, "cost_center")
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
"account": self.closing_account_head,
|
||||
"debit_in_account_currency": abs(net_pl_balance) if net_pl_balance > 0 else 0,
|
||||
"debit": abs(net_pl_balance) if net_pl_balance > 0 else 0,
|
||||
"credit_in_account_currency": abs(net_pl_balance) if net_pl_balance < 0 else 0,
|
||||
"credit": abs(net_pl_balance) if net_pl_balance < 0 else 0
|
||||
"credit": abs(net_pl_balance) if net_pl_balance < 0 else 0,
|
||||
"cost_center": cost_center
|
||||
}))
|
||||
|
||||
from erpnext.accounts.general_ledger import make_gl_entries
|
||||
|
@ -72,6 +72,7 @@ class TestPeriodClosingVoucher(unittest.TestCase):
|
||||
"company": "_Test Company",
|
||||
"fiscal_year": get_fiscal_year(today(), company="_Test Company")[0],
|
||||
"posting_date": today(),
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"remarks": "test"
|
||||
})
|
||||
pcv.insert()
|
||||
|
@ -37,11 +37,25 @@ frappe.ui.form.on('POS Profile', {
|
||||
return { filters: { doc_type: "Sales Invoice", print_format_type: "Js"} };
|
||||
});
|
||||
|
||||
frappe.db.get_value('POS Settings', {name: 'POS Settings'}, 'use_pos_in_offline_mode', (r) => {
|
||||
is_offline = r && cint(r.use_pos_in_offline_mode)
|
||||
frappe.db.get_value('POS Settings', 'POS Settings', 'use_pos_in_offline_mode', (r) => {
|
||||
const is_offline = r && cint(r.use_pos_in_offline_mode)
|
||||
frm.toggle_display('offline_pos_section', is_offline);
|
||||
frm.toggle_display('print_format_for_online', !is_offline);
|
||||
});
|
||||
|
||||
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) {
|
||||
@ -49,11 +63,11 @@ frappe.ui.form.on('POS Profile', {
|
||||
frm.trigger("toggle_display_account_head");
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
company: function(frm) {
|
||||
frm.trigger("toggle_display_account_head");
|
||||
},
|
||||
|
||||
|
||||
toggle_display_account_head: function(frm) {
|
||||
frm.toggle_display('expense_account',
|
||||
erpnext.is_perpetual_inventory_enabled(frm.doc.company));
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -127,25 +127,26 @@ def pos_profile_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
'txt': '%%%s%%' % txt
|
||||
}
|
||||
|
||||
pos_profile = frappe.db.sql("""select pf.name, pf.pos_profile_name
|
||||
pos_profile = frappe.db.sql("""select pf.name
|
||||
from
|
||||
`tabPOS Profile` pf, `tabPOS Profile User` pfu
|
||||
where
|
||||
pfu.parent = pf.name and pfu.user = %(user)s and pf.company = %(company)s
|
||||
and (pf.name like %(txt)s or pf.pos_profile_name like %(txt)s)
|
||||
and (pf.name like %(txt)s)
|
||||
and pf.disabled = 0 limit %(start)s, %(page_len)s""", args)
|
||||
|
||||
if not pos_profile:
|
||||
del args['user']
|
||||
|
||||
pos_profile = frappe.db.sql("""select pf.name, pf.pos_profile_name
|
||||
pos_profile = frappe.db.sql("""select pf.name
|
||||
from
|
||||
`tabPOS Profile` pf left join `tabPOS Profile User` pfu
|
||||
on
|
||||
pf.name = pfu.parent
|
||||
where
|
||||
ifnull(pfu.user, '') = '' and pf.company = %(company)s and
|
||||
(pf.name like %(txt)s or pf.pos_profile_name like %(txt)s)
|
||||
ifnull(pfu.user, '') = ''
|
||||
and pf.company = %(company)s
|
||||
and pf.name like %(txt)s
|
||||
and pf.disabled = 0""", args)
|
||||
|
||||
return pos_profile
|
||||
|
@ -40,7 +40,6 @@ def make_pos_profile():
|
||||
"expense_account": "_Test Account Cost for Goods Sold - _TC",
|
||||
"income_account": "Sales - _TC",
|
||||
"name": "_Test POS Profile",
|
||||
"pos_profile_name": "_Test POS Profile",
|
||||
"naming_series": "_T-POS Profile-",
|
||||
"selling_price_list": "_Test Price List",
|
||||
"territory": "_Test Territory",
|
||||
|
@ -21,8 +21,6 @@ class TestPricingRule(unittest.TestCase):
|
||||
from erpnext.stock.get_item_details import get_item_details
|
||||
from frappe import MandatoryError
|
||||
|
||||
|
||||
|
||||
test_record = {
|
||||
"doctype": "Pricing Rule",
|
||||
"title": "_Test Pricing Rule",
|
||||
@ -93,7 +91,7 @@ class TestPricingRule(unittest.TestCase):
|
||||
|
||||
args.item_code = "_Test Item 2"
|
||||
details = get_item_details(args)
|
||||
self.assertEqual(details.get("discount_percentage"), 15)
|
||||
self.assertEquals(details.get("discount_percentage"), 15)
|
||||
|
||||
def test_pricing_rule_for_margin(self):
|
||||
from erpnext.stock.get_item_details import get_item_details
|
||||
@ -137,8 +135,8 @@ class TestPricingRule(unittest.TestCase):
|
||||
"name": None
|
||||
})
|
||||
details = get_item_details(args)
|
||||
self.assertEqual(details.get("margin_type"), "Percentage")
|
||||
self.assertEqual(details.get("margin_rate_or_amount"), 10)
|
||||
self.assertEquals(details.get("margin_type"), "Percentage")
|
||||
self.assertEquals(details.get("margin_rate_or_amount"), 10)
|
||||
|
||||
def test_pricing_rule_for_variants(self):
|
||||
from erpnext.stock.get_item_details import get_item_details
|
||||
@ -253,6 +251,7 @@ class TestPricingRule(unittest.TestCase):
|
||||
self.assertEqual(so.items[0].rate, 100)
|
||||
|
||||
def test_pricing_rule_with_margin_and_discount(self):
|
||||
frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
|
||||
make_pricing_rule(selling=1, margin_type="Percentage", margin_rate_or_amount=10, discount_percentage=10)
|
||||
si = create_sales_invoice(do_not_save=True)
|
||||
si.items[0].price_list_rate = 1000
|
||||
@ -260,12 +259,11 @@ class TestPricingRule(unittest.TestCase):
|
||||
si.insert(ignore_permissions=True)
|
||||
|
||||
item = si.items[0]
|
||||
self.assertEqual(item.margin_rate_or_amount, 10)
|
||||
self.assertEqual(item.rate_with_margin, 1100)
|
||||
self.assertEquals(item.margin_rate_or_amount, 10)
|
||||
self.assertEquals(item.rate_with_margin, 1100)
|
||||
self.assertEqual(item.discount_percentage, 10)
|
||||
self.assertEqual(item.discount_amount, 110)
|
||||
self.assertEqual(item.rate, 990)
|
||||
|
||||
self.assertEquals(item.discount_amount, 110)
|
||||
self.assertEquals(item.rate, 990)
|
||||
|
||||
def make_pricing_rule(**args):
|
||||
args = frappe._dict(args)
|
||||
@ -295,4 +293,4 @@ def make_pricing_rule(**args):
|
||||
|
||||
applicable_for = doc.applicable_for.replace(' ', '_').lower()
|
||||
if args.get(applicable_for):
|
||||
doc.db_set(applicable_for, args.get(applicable_for))
|
||||
doc.db_set(applicable_for, args.get(applicable_for))
|
||||
|
@ -226,7 +226,7 @@ class PurchaseInvoice(BuyingController):
|
||||
item.expense_account = warehouse_account[item.warehouse]["account"]
|
||||
else:
|
||||
item.expense_account = stock_not_billed_account
|
||||
|
||||
|
||||
elif not item.expense_account and for_validate:
|
||||
throw(_("Expense account is mandatory for item {0}").format(item.item_code or item.item_name))
|
||||
|
||||
@ -379,7 +379,7 @@ class PurchaseInvoice(BuyingController):
|
||||
return gl_entries
|
||||
|
||||
def make_supplier_gl_entry(self, gl_entries):
|
||||
# Checked both rounding_adjustment and rounded_total
|
||||
# Checked both rounding_adjustment and rounded_total
|
||||
# because rounded_total had value even before introcution of posting GLE based on rounded total
|
||||
grand_total = self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total
|
||||
|
||||
@ -859,7 +859,8 @@ def make_stock_entry(source_name, target_doc=None):
|
||||
"Purchase Invoice Item": {
|
||||
"doctype": "Stock Entry Detail",
|
||||
"field_map": {
|
||||
"stock_qty": "transfer_qty"
|
||||
"stock_qty": "transfer_qty",
|
||||
"batch_no": "batch_no"
|
||||
},
|
||||
}
|
||||
}, target_doc)
|
||||
|
@ -313,6 +313,10 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
erpnext.setup_serial_no();
|
||||
},
|
||||
|
||||
packed_items_on_form_rendered: function(doc, grid_row) {
|
||||
erpnext.setup_serial_no();
|
||||
},
|
||||
|
||||
make_sales_return: function() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_sales_return",
|
||||
@ -585,7 +589,7 @@ frappe.ui.form.on('Sales Invoice', {
|
||||
|
||||
frm.set_query('company_address', function(doc) {
|
||||
if(!doc.company) {
|
||||
frappe.throw(_('Please set Company'));
|
||||
frappe.throw(__('Please set Company'));
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -131,7 +131,7 @@ class SalesInvoice(SellingController):
|
||||
#validate amount in mode of payments for returned invoices for pos must be negative
|
||||
if self.is_pos and self.is_return:
|
||||
self.verify_payment_amount_is_negative()
|
||||
|
||||
|
||||
if self.redeem_loyalty_points and self.loyalty_program and self.loyalty_points:
|
||||
validate_loyalty_points(self, self.loyalty_points)
|
||||
|
||||
@ -397,7 +397,7 @@ class SalesInvoice(SellingController):
|
||||
self.account_for_change_amount = pos.get('account_for_change_amount')
|
||||
|
||||
for fieldname in ('territory', 'naming_series', 'currency', 'taxes_and_charges', 'letter_head', 'tc_name',
|
||||
'selling_price_list', 'company', 'select_print_heading', 'cash_bank_account',
|
||||
'selling_price_list', 'company', 'select_print_heading', 'cash_bank_account', 'company_address',
|
||||
'write_off_account', 'write_off_cost_center', 'apply_discount_on'):
|
||||
if (not for_validate) or (for_validate and not self.get(fieldname)):
|
||||
self.set(fieldname, pos.get(fieldname))
|
||||
@ -713,7 +713,7 @@ class SalesInvoice(SellingController):
|
||||
return gl_entries
|
||||
|
||||
def make_customer_gl_entry(self, gl_entries):
|
||||
# Checked both rounding_adjustment and rounded_total
|
||||
# Checked both rounding_adjustment and rounded_total
|
||||
# because rounded_total had value even before introcution of posting GLE based on rounded total
|
||||
grand_total = self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total
|
||||
if grand_total:
|
||||
@ -1026,7 +1026,7 @@ class SalesInvoice(SellingController):
|
||||
def verify_payment_amount_is_negative(self):
|
||||
for entry in self.payments:
|
||||
if entry.amount > 0:
|
||||
frappe.throw(_("Row #{0} (Payment Table): Amount must be negative").format(entry.idx))
|
||||
frappe.throw(_("Row #{0} (Payment Table): Amount must be negative").format(entry.idx))
|
||||
|
||||
# collection of the loyalty points, create the ledger entry for that.
|
||||
def make_loyalty_point_entry(self):
|
||||
|
@ -189,7 +189,7 @@ class ShareTransfer(Document):
|
||||
if (shareholder == 'from_shareholder') else self.to_folio_no;
|
||||
doc.save()
|
||||
else:
|
||||
if doc.folio_no != (self.from_folio_no if (shareholder == 'from_shareholder') else self.to_folio_no):
|
||||
if doc.folio_no and doc.folio_no != (self.from_folio_no if (shareholder == 'from_shareholder') else self.to_folio_no):
|
||||
frappe.throw(_('The folio numbers are not matching'))
|
||||
|
||||
def autoname_folio(self, shareholder, is_company=False):
|
||||
|
@ -79,7 +79,8 @@ class TestShareTransfer(unittest.TestCase):
|
||||
}
|
||||
]
|
||||
for d in share_transfers:
|
||||
frappe.get_doc(d).submit()
|
||||
st = frappe.get_doc(d)
|
||||
st.submit()
|
||||
|
||||
def test_invalid_share_transfer(self):
|
||||
doc = frappe.get_doc({
|
||||
|
@ -333,7 +333,7 @@ def get_due_date_from_template(template_name, posting_date, bill_date):
|
||||
|
||||
def validate_due_date(posting_date, due_date, party_type, party, company=None, bill_date=None, template_name=None):
|
||||
if getdate(due_date) < getdate(posting_date):
|
||||
frappe.throw(_("Due Date cannot be before Posting Date"))
|
||||
frappe.throw(_("Due Date cannot be before Posting / Supplier Invoice Date"))
|
||||
else:
|
||||
if not template_name: return
|
||||
|
||||
@ -457,38 +457,65 @@ def get_timeline_data(doctype, name):
|
||||
|
||||
def get_dashboard_info(party_type, party):
|
||||
current_fiscal_year = get_fiscal_year(nowdate(), as_dict=True)
|
||||
company = frappe.db.get_default("company") or frappe.get_all("Company")[0].name
|
||||
party_account_currency = get_party_account_currency(party_type, party, company)
|
||||
company_default_currency = get_default_currency() \
|
||||
or frappe.get_cached_value('Company', company, 'default_currency')
|
||||
|
||||
if party_account_currency==company_default_currency:
|
||||
total_field = "base_grand_total"
|
||||
else:
|
||||
total_field = "grand_total"
|
||||
|
||||
doctype = "Sales Invoice" if party_type=="Customer" else "Purchase Invoice"
|
||||
|
||||
billing_this_year = frappe.db.sql("""
|
||||
select sum({0})
|
||||
from `tab{1}`
|
||||
where {2}=%s and docstatus=1 and posting_date between %s and %s
|
||||
""".format(total_field, doctype, party_type.lower()),
|
||||
(party, current_fiscal_year.year_start_date, current_fiscal_year.year_end_date))
|
||||
companies = frappe.get_all(doctype, filters={
|
||||
'docstatus': 1,
|
||||
party_type.lower(): party
|
||||
}, distinct=1, fields=['company'])
|
||||
|
||||
total_unpaid = frappe.db.sql("""
|
||||
select sum(debit_in_account_currency) - sum(credit_in_account_currency)
|
||||
company_wise_info = []
|
||||
|
||||
company_wise_grand_total = frappe.get_all(doctype,
|
||||
filters={
|
||||
'docstatus': 1,
|
||||
party_type.lower(): party,
|
||||
'posting_date': ('between', [current_fiscal_year.year_start_date, current_fiscal_year.year_end_date])
|
||||
},
|
||||
group_by="company",
|
||||
fields=["company", "sum(grand_total) as grand_total", "sum(base_grand_total) as base_grand_total"]
|
||||
)
|
||||
|
||||
company_wise_billing_this_year = frappe._dict()
|
||||
|
||||
for d in company_wise_grand_total:
|
||||
company_wise_billing_this_year.setdefault(
|
||||
d.company,{
|
||||
"grand_total": d.grand_total,
|
||||
"base_grand_total": d.base_grand_total
|
||||
})
|
||||
|
||||
|
||||
company_wise_total_unpaid = frappe._dict(frappe.db.sql("""
|
||||
select company, sum(debit_in_account_currency) - sum(credit_in_account_currency)
|
||||
from `tabGL Entry`
|
||||
where party_type = %s and party=%s""", (party_type, party))
|
||||
where party_type = %s and party=%s
|
||||
group by company""", (party_type, party)))
|
||||
|
||||
info = {}
|
||||
info["billing_this_year"] = flt(billing_this_year[0][0]) if billing_this_year else 0
|
||||
info["currency"] = party_account_currency
|
||||
info["total_unpaid"] = flt(total_unpaid[0][0]) if total_unpaid else 0
|
||||
if party_type == "Supplier":
|
||||
info["total_unpaid"] = -1 * info["total_unpaid"]
|
||||
for d in companies:
|
||||
company_default_currency = frappe.db.get_value("Company", d.company, 'default_currency')
|
||||
party_account_currency = get_party_account_currency(party_type, party, d.company)
|
||||
|
||||
return info
|
||||
if party_account_currency==company_default_currency:
|
||||
billing_this_year = flt(company_wise_billing_this_year.get(d.company,{}).get("base_grand_total"))
|
||||
else:
|
||||
billing_this_year = flt(company_wise_billing_this_year.get(d.company,{}).get("grand_total"))
|
||||
|
||||
total_unpaid = flt(company_wise_total_unpaid.get(d.company))
|
||||
|
||||
info = {}
|
||||
info["billing_this_year"] = flt(billing_this_year) if billing_this_year else 0
|
||||
info["currency"] = party_account_currency
|
||||
info["total_unpaid"] = flt(total_unpaid) if total_unpaid else 0
|
||||
info["company"] = d.company
|
||||
|
||||
if party_type == "Supplier":
|
||||
info["total_unpaid"] = -1 * info["total_unpaid"]
|
||||
|
||||
company_wise_info.append(info)
|
||||
|
||||
return company_wise_info
|
||||
|
||||
def get_party_shipping_address(doctype, name):
|
||||
"""
|
||||
@ -508,7 +535,7 @@ def get_party_shipping_address(doctype, name):
|
||||
'dl.link_doctype=%s '
|
||||
'and dl.link_name=%s '
|
||||
'and dl.parenttype="Address" '
|
||||
'and '
|
||||
'and ifnull(ta.disabled, 0) = 0 and'
|
||||
'(ta.address_type="Shipping" or ta.is_shipping_address=1) '
|
||||
'order by ta.is_shipping_address desc, ta.address_type desc limit 1',
|
||||
(doctype, name)
|
||||
|
@ -10,36 +10,6 @@ frappe.query_reports["Accounts Payable"] = {
|
||||
"options": "Company",
|
||||
"default": frappe.defaults.get_user_default("Company")
|
||||
},
|
||||
{
|
||||
"fieldname":"finance_book",
|
||||
"label": __("Finance Book"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Finance Book"
|
||||
},
|
||||
{
|
||||
"fieldname":"supplier",
|
||||
"label": __("Supplier"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Supplier",
|
||||
on_change: () => {
|
||||
var supplier = frappe.query_report.get_filter_value('supplier');
|
||||
frappe.db.get_value('Supplier', supplier, "tax_id", function(value) {
|
||||
frappe.query_report.set_filter_value('tax_id', value["tax_id"]);
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname":"supplier_group",
|
||||
"label": __("Supplier Group"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Supplier Group"
|
||||
},
|
||||
{
|
||||
"fieldname":"report_date",
|
||||
"label": __("As on Date"),
|
||||
"fieldtype": "Date",
|
||||
"default": frappe.datetime.get_today()
|
||||
},
|
||||
{
|
||||
"fieldname":"ageing_based_on",
|
||||
"label": __("Ageing Based On"),
|
||||
@ -48,7 +18,10 @@ frappe.query_reports["Accounts Payable"] = {
|
||||
"default": "Posting Date"
|
||||
},
|
||||
{
|
||||
"fieldtype": "Break",
|
||||
"fieldname":"report_date",
|
||||
"label": __("As on Date"),
|
||||
"fieldtype": "Date",
|
||||
"default": frappe.datetime.get_today()
|
||||
},
|
||||
{
|
||||
"fieldname":"range1",
|
||||
@ -71,6 +44,34 @@ frappe.query_reports["Accounts Payable"] = {
|
||||
"default": "90",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"finance_book",
|
||||
"label": __("Finance Book"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Finance Book"
|
||||
},
|
||||
{
|
||||
"fieldname":"supplier",
|
||||
"label": __("Supplier"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Supplier",
|
||||
on_change: () => {
|
||||
var supplier = frappe.query_report.get_filter_value('supplier');
|
||||
if (supplier) {
|
||||
frappe.db.get_value('Supplier', supplier, "tax_id", function(value) {
|
||||
frappe.query_report.set_filter_value('tax_id', value["tax_id"]);
|
||||
});
|
||||
} else {
|
||||
frappe.query_report.set_filter_value('tax_id', "");
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname":"supplier_group",
|
||||
"label": __("Supplier Group"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Supplier Group"
|
||||
},
|
||||
{
|
||||
"fieldname":"tax_id",
|
||||
"label": __("Tax Id"),
|
||||
|
@ -10,24 +10,6 @@ frappe.query_reports["Accounts Payable Summary"] = {
|
||||
"options": "Company",
|
||||
"default": frappe.defaults.get_user_default("Company")
|
||||
},
|
||||
{
|
||||
"fieldname":"supplier",
|
||||
"label": __("Supplier"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Supplier"
|
||||
},
|
||||
{
|
||||
"fieldname":"supplier_group",
|
||||
"label": __("Supplier Group"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Supplier Group"
|
||||
},
|
||||
{
|
||||
"fieldname":"report_date",
|
||||
"label": __("Date"),
|
||||
"fieldtype": "Date",
|
||||
"default": frappe.datetime.get_today()
|
||||
},
|
||||
{
|
||||
"fieldname":"ageing_based_on",
|
||||
"label": __("Ageing Based On"),
|
||||
@ -36,7 +18,10 @@ frappe.query_reports["Accounts Payable Summary"] = {
|
||||
"default": "Posting Date"
|
||||
},
|
||||
{
|
||||
"fieldtype": "Break",
|
||||
"fieldname":"report_date",
|
||||
"label": __("Date"),
|
||||
"fieldtype": "Date",
|
||||
"default": frappe.datetime.get_today()
|
||||
},
|
||||
{
|
||||
"fieldname":"range1",
|
||||
@ -58,6 +43,24 @@ frappe.query_reports["Accounts Payable Summary"] = {
|
||||
"fieldtype": "Int",
|
||||
"default": "90",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"finance_book",
|
||||
"label": __("Finance Book"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Finance Book"
|
||||
},
|
||||
{
|
||||
"fieldname":"supplier",
|
||||
"label": __("Supplier"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Supplier"
|
||||
},
|
||||
{
|
||||
"fieldname":"supplier_group",
|
||||
"label": __("Supplier Group"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Supplier Group"
|
||||
}
|
||||
],
|
||||
|
||||
|
@ -1,24 +1,14 @@
|
||||
{% if(filters.show_pdc_in_print) { %}
|
||||
<style>
|
||||
@media screen {
|
||||
.print-format {
|
||||
padding: 4mm;
|
||||
font-size: 8.0pt !important;
|
||||
font-family: Tahoma, sans-serif;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% } %}
|
||||
<style>
|
||||
.print-format {
|
||||
padding: 4mm;
|
||||
font-size: 8.0pt !important;
|
||||
font-family: Tahoma, sans-serif;
|
||||
}
|
||||
.print-format {
|
||||
padding: 4mm;
|
||||
font-size: 8.0pt !important;
|
||||
}
|
||||
.print-format td {
|
||||
vertical-align:middle !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<h2 class="text-center">{%= __(report.report_name) %}</h2>
|
||||
<h2 class="text-center" style="margin-top:0">{%= __(report.report_name) %}</h2>
|
||||
<h4 class="text-center">
|
||||
{% if (filters.customer_name) { %}
|
||||
{%= filters.customer_name %}
|
||||
@ -36,7 +26,20 @@
|
||||
{%= __("Until") %}
|
||||
{%= frappe.datetime.str_to_user(filters.report_date) %}
|
||||
</h5>
|
||||
<hr>
|
||||
|
||||
<div class="clearfix">
|
||||
<div class="pull-left">
|
||||
{% if(filters.payment_terms) { %}
|
||||
<strong>{%= __("Payment Terms") %}:</strong> {%= filters.payment_terms %}
|
||||
{% } %}
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
{% if(filters.credit_limit) { %}
|
||||
<strong>{%= __("Credit Limit") %}:</strong> {%= format_currency(filters.credit_limit) %}
|
||||
{% } %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if(filters.show_pdc_in_print) { %}
|
||||
{% var balance_row = data.slice(-1).pop();
|
||||
var range1 = report.columns[11].label;
|
||||
@ -104,17 +107,21 @@
|
||||
<thead>
|
||||
<tr>
|
||||
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
|
||||
<th style="width: 18%">{%= __("Date") %}</th>
|
||||
<th style="width: 17%">{%= __("Reference") %}</th>
|
||||
<th style="width: 7%">{%= __("Date") %}</th>
|
||||
<th style="width: 7%">{%= __("Age (Days)") %}</th>
|
||||
<th style="width: 13%">{%= __("Reference") %}</th>
|
||||
{% if(report.report_name === "Accounts Receivable") { %}
|
||||
<th style="width: 10%">{%= __("Sales Person") %}</th>
|
||||
{% } %}
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
|
||||
{% } %}
|
||||
<th style="width: 10%">{%= __("Invoiced Amount") %}</th>
|
||||
<th style="width: 10%; text-align: right">{%= __("Invoiced Amount") %}</th>
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
<th style="width: 10%">{%= __("Paid Amount") %}</th>
|
||||
<th style="width: 10%">{%= report.report_name === "Accounts Receivable" ? __('Credit Note') : __('Debit Note') %}</th>
|
||||
<th style="width: 10%; text-align: right">{%= __("Paid Amount") %}</th>
|
||||
<th style="width: 10%; text-align: right">{%= report.report_name === "Accounts Receivable" ? __('Credit Note') : __('Debit Note') %}</th>
|
||||
{% } %}
|
||||
<th style="width: 15%">{%= __("Outstanding Amount") %}</th>
|
||||
<th style="width: 15%; text-align: right">{%= __("Outstanding Amount") %}</th>
|
||||
{% if(filters.show_pdc_in_print) { %}
|
||||
{% if(report.report_name === "Accounts Receivable") { %}
|
||||
<th style="width: 10%">{%= __("Customer LPO No.") %}</th>
|
||||
@ -139,6 +146,7 @@
|
||||
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
|
||||
{% if(data[i][__("Customer")] || data[i][__("Supplier")]) { %}
|
||||
<td>{%= frappe.datetime.str_to_user(data[i]["posting_date"]) %}</td>
|
||||
<td style="text-align: right">{%= data[i][__("Age (Days)")] %}</td>
|
||||
<td>
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
{%= data[i]["voucher_type"] %}
|
||||
@ -146,6 +154,11 @@
|
||||
{% } %}
|
||||
{%= data[i]["voucher_no"] %}
|
||||
</td>
|
||||
|
||||
{% if(report.report_name === "Accounts Receivable") { %}
|
||||
<td>{%= data[i]["sales_person"] %}</td>
|
||||
{% } %}
|
||||
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
<td>
|
||||
{% if(!(filters.customer || filters.supplier)) { %}
|
||||
@ -156,10 +169,15 @@
|
||||
<br> {%= data[i][__("Supplier Name")] %}
|
||||
{% } %}
|
||||
{% } %}
|
||||
<br>{%= __("Remarks") %}:
|
||||
{%= data[i][__("Remarks")] %}
|
||||
<div>
|
||||
{% if data[i][__("Remarks")] %}
|
||||
{%= __("Remarks") %}:
|
||||
{%= data[i][__("Remarks")] %}
|
||||
{% } %}
|
||||
</div>
|
||||
</td>
|
||||
{% } %}
|
||||
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["invoiced_amount"], data[i]["currency"]) %}</td>
|
||||
|
||||
@ -187,14 +205,18 @@
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
<td></td>
|
||||
{% } %}
|
||||
<td><b>{%= __("Total") %}</b></td>
|
||||
{% if(report.report_name === "Accounts Receivable") { %}
|
||||
<td></td>
|
||||
{% } %}
|
||||
<td></td>
|
||||
<td style="text-align: right"><b>{%= __("Total") %}</b></td>
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["invoiced_amount"], data[i]["currency"] ) %}</td>
|
||||
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["paid_amount"], data[i]["currency"]) %}</td>
|
||||
<td style="text-align: right">{%= report.report_name === "Accounts Receivable" ? format_currency(data[i]["credit_note"], data[i]["currency"]) : format_currency(data[i]["Debit Note"], data[i]["currency"]) %} </td>
|
||||
<td style="text-align: right">{%= report.report_name === "Accounts Receivable" ? format_currency(data[i]["credit_note"], data[i]["currency"]) : format_currency(data[i]["debit_note"], data[i]["currency"]) %} </td>
|
||||
{% } %}
|
||||
<td style="text-align: right">
|
||||
{%= format_currency(data[i]["outstanding_amount"], data[i]["currency"]) %}</td>
|
||||
|
@ -10,6 +10,40 @@ frappe.query_reports["Accounts Receivable"] = {
|
||||
"options": "Company",
|
||||
"default": frappe.defaults.get_user_default("Company")
|
||||
},
|
||||
{
|
||||
"fieldname":"ageing_based_on",
|
||||
"label": __("Ageing Based On"),
|
||||
"fieldtype": "Select",
|
||||
"options": 'Posting Date\nDue Date',
|
||||
"default": "Posting Date"
|
||||
},
|
||||
{
|
||||
"fieldname":"report_date",
|
||||
"label": __("As on Date"),
|
||||
"fieldtype": "Date",
|
||||
"default": frappe.datetime.get_today()
|
||||
},
|
||||
{
|
||||
"fieldname":"range1",
|
||||
"label": __("Ageing Range 1"),
|
||||
"fieldtype": "Int",
|
||||
"default": "30",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"range2",
|
||||
"label": __("Ageing Range 2"),
|
||||
"fieldtype": "Int",
|
||||
"default": "60",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"range3",
|
||||
"label": __("Ageing Range 3"),
|
||||
"fieldtype": "Int",
|
||||
"default": "90",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"finance_book",
|
||||
"label": __("Finance Book"),
|
||||
@ -23,10 +57,19 @@ frappe.query_reports["Accounts Receivable"] = {
|
||||
"options": "Customer",
|
||||
on_change: () => {
|
||||
var customer = frappe.query_report.get_filter_value('customer');
|
||||
frappe.db.get_value('Customer', customer, ["tax_id", "customer_name"], function(value) {
|
||||
frappe.query_report.set_filter_value('tax_id', value["tax_id"]);
|
||||
frappe.query_report.set_filter_value('customer_name', value["customer_name"]);
|
||||
});
|
||||
if (customer) {
|
||||
frappe.db.get_value('Customer', customer, ["tax_id", "customer_name", "credit_limit", "payment_terms"], function(value) {
|
||||
frappe.query_report.set_filter_value('tax_id', value["tax_id"]);
|
||||
frappe.query_report.set_filter_value('customer_name', value["customer_name"]);
|
||||
frappe.query_report.set_filter_value('credit_limit', value["credit_limit"]);
|
||||
frappe.query_report.set_filter_value('payment_terms', value["payment_terms"]);
|
||||
});
|
||||
} else {
|
||||
frappe.query_report.set_filter_value('tax_id', "");
|
||||
frappe.query_report.set_filter_value('customer_name', "");
|
||||
frappe.query_report.set_filter_value('credit_limit', "");
|
||||
frappe.query_report.set_filter_value('payment_terms', "");
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -59,43 +102,6 @@ frappe.query_reports["Accounts Receivable"] = {
|
||||
"fieldtype": "Link",
|
||||
"options": "Sales Person"
|
||||
},
|
||||
{
|
||||
"fieldtype": "Break",
|
||||
},
|
||||
{
|
||||
"fieldname":"report_date",
|
||||
"label": __("As on Date"),
|
||||
"fieldtype": "Date",
|
||||
"default": frappe.datetime.get_today()
|
||||
},
|
||||
{
|
||||
"fieldname":"ageing_based_on",
|
||||
"label": __("Ageing Based On"),
|
||||
"fieldtype": "Select",
|
||||
"options": 'Posting Date\nDue Date',
|
||||
"default": "Posting Date"
|
||||
},
|
||||
{
|
||||
"fieldname":"range1",
|
||||
"label": __("Ageing Range 1"),
|
||||
"fieldtype": "Int",
|
||||
"default": "30",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"range2",
|
||||
"label": __("Ageing Range 2"),
|
||||
"fieldtype": "Int",
|
||||
"default": "60",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"range3",
|
||||
"label": __("Ageing Range 3"),
|
||||
"fieldtype": "Int",
|
||||
"default": "90",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"show_pdc_in_print",
|
||||
"label": __("Show PDC in Print"),
|
||||
@ -112,6 +118,18 @@ frappe.query_reports["Accounts Receivable"] = {
|
||||
"label": __("Customer Name"),
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"payment_terms",
|
||||
"label": __("Payment Tems"),
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"credit_limit",
|
||||
"label": __("Credit Limit"),
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 1
|
||||
}
|
||||
],
|
||||
|
||||
|
@ -125,17 +125,22 @@ class ReceivablePayableReport(object):
|
||||
}]
|
||||
|
||||
if args.get('party_type') == 'Customer':
|
||||
columns.append({
|
||||
"label": _("Customer LPO"),
|
||||
"fieldtype": "Data",
|
||||
"fieldname": "po_no",
|
||||
"width": 100,
|
||||
})
|
||||
columns += [_("Delivery Note") + ":Data:100"]
|
||||
if args.get("party_type") == "Customer":
|
||||
columns += [
|
||||
{
|
||||
"label": _("Customer LPO"),
|
||||
"fieldtype": "Data",
|
||||
"fieldname": "po_no",
|
||||
"width": 100,
|
||||
},
|
||||
_("Delivery Note") + ":Data:100",
|
||||
_("Territory") + ":Link/Territory:80",
|
||||
_("Customer Group") + ":Link/Customer Group:120"
|
||||
_("Customer Group") + ":Link/Customer Group:120",
|
||||
{
|
||||
"label": _("Sales Person"),
|
||||
"fieldtype": "Data",
|
||||
"fieldname": "sales_person",
|
||||
"width": 120,
|
||||
}
|
||||
]
|
||||
if args.get("party_type") == "Supplier":
|
||||
columns += [_("Supplier Group") + ":Link/Supplier Group:80"]
|
||||
@ -238,7 +243,8 @@ class ReceivablePayableReport(object):
|
||||
|
||||
# customer territory / supplier group
|
||||
if args.get("party_type") == "Customer":
|
||||
row += [self.get_territory(gle.party), self.get_customer_group(gle.party)]
|
||||
row += [self.get_territory(gle.party), self.get_customer_group(gle.party),
|
||||
voucher_details.get(gle.voucher_no, {}).get("sales_person")]
|
||||
if args.get("party_type") == "Supplier":
|
||||
row += [self.get_supplier_group(gle.party)]
|
||||
|
||||
@ -395,9 +401,14 @@ class ReceivablePayableReport(object):
|
||||
values.append(self.filters.get("sales_partner"))
|
||||
|
||||
if self.filters.get("sales_person"):
|
||||
conditions.append("""party in (select parent
|
||||
from `tabSales Team` where sales_person=%s and parenttype = 'Customer')""")
|
||||
values.append(self.filters.get("sales_person"))
|
||||
lft, rgt = frappe.db.get_value("Sales Person",
|
||||
self.filters.get("sales_person"), ["lft", "rgt"])
|
||||
|
||||
conditions.append("""exists(select name from `tabSales Team` steam where
|
||||
steam.sales_person in (select name from `tabSales Person` where lft >= {0} and rgt <= {1})
|
||||
and ((steam.parent = voucher_no and steam.parenttype = voucher_type)
|
||||
or (steam.parent = against_voucher and steam.parenttype = against_voucher_type)
|
||||
or (steam.parent = party and steam.parenttype = 'Customer')))""".format(lft, rgt))
|
||||
|
||||
if party_type_field=="supplier":
|
||||
if self.filters.get("supplier_group"):
|
||||
@ -550,8 +561,12 @@ def get_voucher_details(party_type, voucher_nos, dn_details):
|
||||
voucher_details = frappe._dict()
|
||||
|
||||
if party_type == "Customer":
|
||||
for si in frappe.db.sql("""select name, due_date, po_no
|
||||
from `tabSales Invoice` where docstatus=1 and name in (%s)
|
||||
for si in frappe.db.sql("""
|
||||
select inv.name, inv.due_date, inv.po_no, GROUP_CONCAT(steam.sales_person SEPARATOR ', ') as sales_person
|
||||
from `tabSales Invoice` inv
|
||||
left join `tabSales Team` steam on steam.parent = inv.name and steam.parenttype = 'Sales Invoice'
|
||||
where inv.docstatus=1 and inv.name in (%s)
|
||||
group by inv.name
|
||||
""" %(','.join(['%s'] *len(voucher_nos))), (tuple(voucher_nos)), as_dict=1):
|
||||
si['delivery_note'] = dn_details.get(si.name)
|
||||
voucher_details.setdefault(si.name, si)
|
||||
|
@ -11,25 +11,11 @@ frappe.query_reports["Accounts Receivable Summary"] = {
|
||||
"default": frappe.defaults.get_user_default("Company")
|
||||
},
|
||||
{
|
||||
"fieldname":"customer",
|
||||
"label": __("Customer"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Customer"
|
||||
},
|
||||
{
|
||||
"fieldname":"customer_group",
|
||||
"label": __("Customer Group"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Customer Group"
|
||||
},
|
||||
{
|
||||
"fieldname":"payment_terms_template",
|
||||
"label": __("Payment Terms Template"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Payment Terms Template"
|
||||
},
|
||||
{
|
||||
"fieldtype": "Break",
|
||||
"fieldname":"ageing_based_on",
|
||||
"label": __("Ageing Based On"),
|
||||
"fieldtype": "Select",
|
||||
"options": 'Posting Date\nDue Date',
|
||||
"default": "Posting Date"
|
||||
},
|
||||
{
|
||||
"fieldname":"report_date",
|
||||
@ -37,13 +23,6 @@ frappe.query_reports["Accounts Receivable Summary"] = {
|
||||
"fieldtype": "Date",
|
||||
"default": frappe.datetime.get_today()
|
||||
},
|
||||
{
|
||||
"fieldname":"ageing_based_on",
|
||||
"label": __("Ageing Based On"),
|
||||
"fieldtype": "Select",
|
||||
"options": 'Posting Date\nDue Date',
|
||||
"default": "Posting Date"
|
||||
},
|
||||
{
|
||||
"fieldname":"range1",
|
||||
"label": __("Ageing Range 1"),
|
||||
@ -64,6 +43,48 @@ frappe.query_reports["Accounts Receivable Summary"] = {
|
||||
"fieldtype": "Int",
|
||||
"default": "90",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"finance_book",
|
||||
"label": __("Finance Book"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Finance Book"
|
||||
},
|
||||
{
|
||||
"fieldname":"customer",
|
||||
"label": __("Customer"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Customer"
|
||||
},
|
||||
{
|
||||
"fieldname":"customer_group",
|
||||
"label": __("Customer Group"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Customer Group"
|
||||
},
|
||||
{
|
||||
"fieldname":"payment_terms_template",
|
||||
"label": __("Payment Terms Template"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Payment Terms Template"
|
||||
},
|
||||
{
|
||||
"fieldname":"territory",
|
||||
"label": __("Territory"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Territory"
|
||||
},
|
||||
{
|
||||
"fieldname":"sales_partner",
|
||||
"label": __("Sales Partner"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Sales Partner"
|
||||
},
|
||||
{
|
||||
"fieldname":"sales_person",
|
||||
"label": __("Sales Person"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Sales Person"
|
||||
}
|
||||
],
|
||||
|
||||
|
@ -22,7 +22,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
|
||||
columns += [ args.get("party_type") + " Name::140"]
|
||||
|
||||
credit_debit_label = "Credit Note Amt" if args.get('party_type') == 'Customer' else "Debit Note Amt"
|
||||
|
||||
|
||||
columns += [{
|
||||
"label": _("Total Invoiced Amt"),
|
||||
"fieldname": "total_invoiced_amt",
|
||||
@ -97,6 +97,12 @@ class AccountsReceivableSummary(ReceivablePayableReport):
|
||||
"fieldtype": "Link",
|
||||
"options": "Customer Group",
|
||||
"width": 80
|
||||
},
|
||||
{
|
||||
"label": _("Sales Person"),
|
||||
"fieldtype": "Data",
|
||||
"fieldname": "sales_person",
|
||||
"width": 120,
|
||||
}]
|
||||
|
||||
if args.get("party_type") == "Supplier":
|
||||
@ -107,7 +113,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
|
||||
"options": "Supplier Group",
|
||||
"width": 80
|
||||
}]
|
||||
|
||||
|
||||
columns.append({
|
||||
"fieldname": "currency",
|
||||
"label": _("Currency"),
|
||||
@ -135,10 +141,10 @@ class AccountsReceivableSummary(ReceivablePayableReport):
|
||||
]
|
||||
|
||||
if args.get("party_type") == "Customer":
|
||||
row += [self.get_territory(party), self.get_customer_group(party)]
|
||||
row += [self.get_territory(party), self.get_customer_group(party), ", ".join(set(party_dict.sales_person))]
|
||||
if args.get("party_type") == "Supplier":
|
||||
row += [self.get_supplier_group(party)]
|
||||
|
||||
|
||||
row.append(party_dict.currency)
|
||||
data.append(row)
|
||||
|
||||
@ -156,15 +162,19 @@ class AccountsReceivableSummary(ReceivablePayableReport):
|
||||
"range1": 0,
|
||||
"range2": 0,
|
||||
"range3": 0,
|
||||
"range4": 0
|
||||
"range4": 0,
|
||||
"sales_person": []
|
||||
})
|
||||
)
|
||||
for k in list(party_total[d.party]):
|
||||
if k != "currency":
|
||||
if k not in ["currency", "sales_person"]:
|
||||
party_total[d.party][k] += flt(d.get(k, 0))
|
||||
|
||||
|
||||
party_total[d.party].currency = d.currency
|
||||
|
||||
if d.sales_person:
|
||||
party_total[d.party].sales_person.append(d.sales_person)
|
||||
|
||||
return party_total
|
||||
|
||||
def get_voucherwise_data(self, party_naming_by, args):
|
||||
@ -181,12 +191,13 @@ class AccountsReceivableSummary(ReceivablePayableReport):
|
||||
cols += ["bill_no", "bill_date"]
|
||||
|
||||
cols += ["invoiced_amt", "paid_amt", "credit_amt",
|
||||
"outstanding_amt", "age", "range1", "range2", "range3", "range4", "currency"]
|
||||
"outstanding_amt", "age", "range1", "range2", "range3", "range4", "currency", "pdc/lc_date", "pdc/lc_ref",
|
||||
"pdc/lc_amount", "remaining_balance"]
|
||||
|
||||
if args.get("party_type") == "Supplier":
|
||||
cols += ["supplier_group", "remarks"]
|
||||
if args.get("party_type") == "Customer":
|
||||
cols += ["territory", "customer_group", "remarks"]
|
||||
cols += ["po_no", "do_no", "territory", "customer_group", "sales_person", "remarks"]
|
||||
|
||||
return self.make_data_dict(cols, voucherwise_data)
|
||||
|
||||
|
@ -16,8 +16,6 @@ def execute(filters=None):
|
||||
return [], []
|
||||
|
||||
account_details = {}
|
||||
if not filters.get("group_by"):
|
||||
filters['group_by'] = _('Group by Voucher (Consolidated)')
|
||||
|
||||
if filters and filters.get('print_in_account_currency') and \
|
||||
not filters.get('account'):
|
||||
|
@ -236,7 +236,7 @@ class GrossProfitGenerator(object):
|
||||
previous_stock_value = len(my_sle) > i+1 and \
|
||||
flt(my_sle[i+1].stock_value) or 0.0
|
||||
if previous_stock_value:
|
||||
return previous_stock_value - flt(sle.stock_value)
|
||||
return (previous_stock_value - flt(sle.stock_value)) * flt(row.qty) / abs(flt(sle.qty))
|
||||
else:
|
||||
return flt(row.qty) * self.get_average_buying_rate(row, item_code)
|
||||
else:
|
||||
|
@ -231,6 +231,13 @@ def get_columns():
|
||||
"options": "Account",
|
||||
"width": 300
|
||||
},
|
||||
{
|
||||
"fieldname": "currency",
|
||||
"label": _("Currency"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Currency",
|
||||
"hidden": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "opening_debit",
|
||||
"label": _("Opening (Dr)"),
|
||||
@ -272,13 +279,6 @@ def get_columns():
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 120
|
||||
},
|
||||
{
|
||||
"fieldname": "currency",
|
||||
"label": _("Currency"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Currency",
|
||||
"hidden": 1
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -0,0 +1,30 @@
|
||||
{
|
||||
"add_total_row": 1,
|
||||
"creation": "2018-11-22 16:53:19.167935",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2018-11-22 17:40:11.317567",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Trial Balance (Simple)",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"query": "select fiscal_year as \"Fiscal Year:Data:80\",\n\tcompany as \"Company:Data:220\",\n\tposting_date as \"Posting Date:Date:100\",\n\taccount as \"Account:Data:380\",\n\tsum(debit) as \"Debit:Currency:140\",\n\tsum(credit) as \"Credit:Currency:140\"\nfrom `tabGL Entry`\ngroup by fiscal_year, company, posting_date, account\norder by fiscal_year, company, posting_date, account",
|
||||
"ref_doctype": "GL Entry",
|
||||
"report_name": "Trial Balance (Simple)",
|
||||
"report_type": "Query Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Accounts User"
|
||||
},
|
||||
{
|
||||
"role": "Accounts Manager"
|
||||
},
|
||||
{
|
||||
"role": "Auditor"
|
||||
}
|
||||
]
|
||||
}
|
@ -315,7 +315,6 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
method: "erpnext.stock.doctype.material_request.material_request.make_purchase_order",
|
||||
source_doctype: "Material Request",
|
||||
target: me.frm,
|
||||
args: args,
|
||||
setters: {
|
||||
company: me.frm.doc.company
|
||||
},
|
||||
|
@ -1 +0,0 @@
|
||||
Trends of purchases across Items, Item Groups, Suppliers.
|
@ -1 +0,0 @@
|
||||
from __future__ import unicode_literals
|
@ -1,231 +0,0 @@
|
||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
frappe.pages['purchase-analytics'].on_page_load = function(wrapper) {
|
||||
frappe.ui.make_app_page({
|
||||
parent: wrapper,
|
||||
title: __('Purchase Analytics'),
|
||||
single_column: true
|
||||
});
|
||||
|
||||
new erpnext.PurchaseAnalytics(wrapper);
|
||||
|
||||
frappe.breadcrumbs.add("Buying");
|
||||
}
|
||||
|
||||
erpnext.PurchaseAnalytics = frappe.views.TreeGridReport.extend({
|
||||
init: function(wrapper) {
|
||||
this._super({
|
||||
title: __("Purchase Analytics"),
|
||||
parent: $(wrapper).find('.layout-main'),
|
||||
page: wrapper.page,
|
||||
doctypes: ["Item", "Item Group", "Supplier", "Supplier Group", "Company", "Fiscal Year",
|
||||
"Purchase Invoice", "Purchase Invoice Item",
|
||||
"Purchase Order", "Purchase Order Item[Purchase Analytics]",
|
||||
"Purchase Receipt", "Purchase Receipt Item[Purchase Analytics]"],
|
||||
tree_grid: { show: true }
|
||||
});
|
||||
|
||||
this.tree_grids = {
|
||||
"Supplier Group": {
|
||||
label: __("Supplier Group / Supplier"),
|
||||
show: true,
|
||||
item_key: "supplier",
|
||||
parent_field: "parent_supplier_group",
|
||||
formatter: function(item) {
|
||||
return item.supplier_name ? item.supplier_name + " (" + item.name + ")" : item.name;
|
||||
}
|
||||
},
|
||||
"Supplier": {
|
||||
label: __("Supplier"),
|
||||
show: false,
|
||||
item_key: "supplier",
|
||||
formatter: function(item) {
|
||||
return item.supplier_name ? item.supplier_name + " (" + item.name + ")" : item.name;
|
||||
}
|
||||
},
|
||||
"Item Group": {
|
||||
label: "Item",
|
||||
show: true,
|
||||
parent_field: "parent_item_group",
|
||||
item_key: "item_code",
|
||||
formatter: function(item) {
|
||||
return item.name;
|
||||
}
|
||||
},
|
||||
"Item": {
|
||||
label: "Item",
|
||||
show: false,
|
||||
item_key: "item_code",
|
||||
formatter: function(item) {
|
||||
return item.name;
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
setup_columns: function() {
|
||||
this.tree_grid = this.tree_grids[this.tree_type];
|
||||
|
||||
var std_columns = [
|
||||
{id: "name", name: this.tree_grid.label, field: "name", width: 300},
|
||||
{id: "total", name: "Total", field: "total", plot: false,
|
||||
formatter: this.currency_formatter}
|
||||
];
|
||||
|
||||
this.make_date_range_columns();
|
||||
this.columns = std_columns.concat(this.columns);
|
||||
},
|
||||
filters: [
|
||||
{fieldtype:"Select", label: __("Tree Type"), fieldname: "tree_type",
|
||||
options:["Supplier Group", "Supplier", "Item Group", "Item"],
|
||||
filter: function(val, item, opts, me) {
|
||||
return me.apply_zero_filter(val, item, opts, me);
|
||||
}},
|
||||
{fieldtype:"Select", label: __("Based On"), fieldname: "based_on",
|
||||
options:["Purchase Invoice", "Purchase Order", "Purchase Receipt"]},
|
||||
{fieldtype:"Select", label: __("Value or Qty"), fieldname: "value_or_qty",
|
||||
options:["Value", "Quantity"]},
|
||||
{fieldtype:"Select", label: __("Company"), link:"Company", fieldname: "company",
|
||||
default_value: __("Select Company...")},
|
||||
{fieldtype:"Date", label: __("From Date"), fieldname: "from_date"},
|
||||
{fieldtype:"Date", label: __("To Date"), fieldname: "to_date"},
|
||||
{fieldtype:"Select", label: __("Range"), fieldname: "range",
|
||||
options:[{label: __("Daily"), value: "Daily"}, {label: __("Weekly"), value: "Weekly"},
|
||||
{label: __("Monthly"), value: "Monthly"}, {label: __("Quarterly"), value: "Quarterly"},
|
||||
{label: __("Yearly"), value: "Yearly"}]}
|
||||
],
|
||||
setup_filters: function() {
|
||||
var me = this;
|
||||
this._super();
|
||||
|
||||
this.trigger_refresh_on_change(["value_or_qty", "tree_type", "based_on", "company"]);
|
||||
|
||||
this.show_zero_check();
|
||||
},
|
||||
init_filter_values: function() {
|
||||
this._super();
|
||||
this.filter_inputs.range.val('Monthly');
|
||||
},
|
||||
prepare_data: function() {
|
||||
var me = this;
|
||||
if (!this.tl) {
|
||||
// add 'Not Set' Supplier & Item
|
||||
// (Supplier / Item are not mandatory!!)
|
||||
frappe.report_dump.data["Supplier"].push({
|
||||
name: __("Not Set"),
|
||||
parent_supplier_group: __("All Supplier Groups"),
|
||||
id: "Not Set",
|
||||
});
|
||||
|
||||
frappe.report_dump.data["Item"].push({
|
||||
name: __("Not Set"),
|
||||
parent_item_group: "All Item Groups",
|
||||
id: "Not Set",
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.tl || !this.tl[this.based_on]) {
|
||||
this.make_transaction_list(this.based_on, this.based_on + " Item");
|
||||
}
|
||||
|
||||
|
||||
if(!this.data || me.item_type != me.tree_type) {
|
||||
var items;
|
||||
if(me.tree_type=='Supplier') {
|
||||
items = frappe.report_dump.data["Supplier"];
|
||||
} else if(me.tree_type=='Supplier Group') {
|
||||
items = this.prepare_tree("Supplier", "Supplier Group");
|
||||
} else if(me.tree_type=="Item Group") {
|
||||
items = this.prepare_tree("Item", "Item Group");
|
||||
} else if(me.tree_type=="Item") {
|
||||
items = frappe.report_dump.data["Item"];
|
||||
}
|
||||
|
||||
me.item_type = me.tree_type
|
||||
me.parent_map = {};
|
||||
me.item_by_name = {};
|
||||
me.data = [];
|
||||
|
||||
$.each(items, function(i, v) {
|
||||
var d = copy_dict(v);
|
||||
|
||||
me.data.push(d);
|
||||
me.item_by_name[d.name] = d;
|
||||
if(d[me.tree_grid.parent_field]) {
|
||||
me.parent_map[d.name] = d[me.tree_grid.parent_field];
|
||||
}
|
||||
me.reset_item_values(d);
|
||||
});
|
||||
|
||||
this.set_indent();
|
||||
|
||||
} else {
|
||||
// otherwise, only reset values
|
||||
$.each(this.data, function(i, d) {
|
||||
me.reset_item_values(d);
|
||||
});
|
||||
}
|
||||
|
||||
this.prepare_balances();
|
||||
if(me.tree_grid.show) {
|
||||
this.set_totals(false);
|
||||
this.update_groups();
|
||||
} else {
|
||||
this.set_totals(true);
|
||||
}
|
||||
},
|
||||
prepare_balances: function() {
|
||||
var me = this;
|
||||
var from_date = frappe.datetime.str_to_obj(this.from_date);
|
||||
var to_date = frappe.datetime.str_to_obj(this.to_date);
|
||||
var is_val = this.value_or_qty == 'Value';
|
||||
|
||||
$.each(this.tl[this.based_on], function(i, tl) {
|
||||
if (me.is_default('company') ? true : tl.company === me.company) {
|
||||
var posting_date = frappe.datetime.str_to_obj(tl.posting_date);
|
||||
if (posting_date >= from_date && posting_date <= to_date) {
|
||||
var item = me.item_by_name[tl[me.tree_grid.item_key]] ||
|
||||
me.item_by_name['Not Set'];
|
||||
item[me.column_map[tl.posting_date].field] += (is_val ? tl.base_net_amount : tl.qty);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
update_groups: function() {
|
||||
var me = this;
|
||||
|
||||
$.each(this.data, function(i, item) {
|
||||
var parent = me.parent_map[item.name];
|
||||
while(parent) {
|
||||
var parent_group = me.item_by_name[parent];
|
||||
|
||||
$.each(me.columns, function(c, col) {
|
||||
if (col.formatter == me.currency_formatter) {
|
||||
parent_group[col.field] =
|
||||
flt(parent_group[col.field])
|
||||
+ flt(item[col.field]);
|
||||
}
|
||||
});
|
||||
parent = me.parent_map[parent];
|
||||
}
|
||||
});
|
||||
},
|
||||
set_totals: function(sort) {
|
||||
var me = this;
|
||||
var checked = false;
|
||||
$.each(this.data, function(i, d) {
|
||||
d.total = 0.0;
|
||||
$.each(me.columns, function(i, col) {
|
||||
if(col.formatter==me.currency_formatter && !col.hidden && col.field!="total")
|
||||
d.total += d[col.field];
|
||||
if(d.checked) checked = true;
|
||||
})
|
||||
});
|
||||
|
||||
if(sort)this.data = this.data.sort(function(a, b) { return b.total - a.total; });
|
||||
|
||||
if(!this.checked) {
|
||||
this.data[0].checked = true;
|
||||
}
|
||||
}
|
||||
});
|
@ -1,23 +0,0 @@
|
||||
{
|
||||
"creation": "2012-09-21 20:15:16.000000",
|
||||
"docstatus": 0,
|
||||
"doctype": "Page",
|
||||
"icon": "fa fa-bar-chart",
|
||||
"idx": 1,
|
||||
"modified": "2013-07-11 14:43:52.000000",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "purchase-analytics",
|
||||
"owner": "Administrator",
|
||||
"page_name": "purchase-analytics",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Analytics"
|
||||
},
|
||||
{
|
||||
"role": "Purchase Manager"
|
||||
}
|
||||
],
|
||||
"standard": "Yes",
|
||||
"title": "Purchase Analytics"
|
||||
}
|
@ -68,11 +68,8 @@ frappe.query_reports["Purchase Analytics"] = {
|
||||
}
|
||||
|
||||
],
|
||||
"formatter": function(value, row, column, data) {
|
||||
if(!value){
|
||||
value = 0
|
||||
}
|
||||
return value;
|
||||
after_datatable_render: function(datatable_obj) {
|
||||
$(datatable_obj.wrapper).find(".dt-row-0").find('input[type=checkbox]').click();
|
||||
},
|
||||
get_datatable_options(options) {
|
||||
return Object.assign(options, {
|
||||
@ -110,19 +107,19 @@ frappe.query_reports["Purchase Analytics"] = {
|
||||
labels: raw_data.labels,
|
||||
datasets: new_datasets
|
||||
}
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
frappe.query_report.chart.update(new_data)
|
||||
},200)
|
||||
|
||||
|
||||
},500)
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
frappe.query_report.chart.draw(true);
|
||||
}, 800)
|
||||
}, 1000)
|
||||
|
||||
frappe.query_report.raw_chart_data = new_data;
|
||||
},
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
from __future__ import unicode_literals
|
||||
from frappe import _
|
||||
import frappe
|
||||
|
||||
|
||||
def get_data():
|
||||
return [
|
||||
config = [
|
||||
{
|
||||
"label": _("Billing"),
|
||||
"items": [
|
||||
@ -25,48 +27,7 @@ def get_data():
|
||||
"type": "doctype",
|
||||
"name": "Payment Entry",
|
||||
"description": _("Bank/Cash transactions against party or for internal transfer")
|
||||
},
|
||||
{
|
||||
"type": "page",
|
||||
"name": "pos",
|
||||
"label": _("POS"),
|
||||
"description": _("Point of Sale")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Cashier Closing",
|
||||
"description": _("Cashier Closing")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Auto Repeat",
|
||||
"label": _("Auto Repeat"),
|
||||
"description": _("To make recurring documents")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Loyalty Program",
|
||||
"label": _("Loyalty Program"),
|
||||
"description": _("To make Customer based incentive schemes.")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Loyalty Point Entry",
|
||||
"label": _("Loyalty Point Entry"),
|
||||
"description": _("To view logs of Loyalty Points assigned to a Customer.")
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "Accounts Receivable",
|
||||
"doctype": "Sales Invoice",
|
||||
"is_query_report": True
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "Accounts Payable",
|
||||
"doctype": "Purchase Invoice",
|
||||
"is_query_report": True
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
},
|
||||
@ -93,7 +54,7 @@ def get_data():
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name":"General Ledger",
|
||||
"name": "General Ledger",
|
||||
"doctype": "GL Entry",
|
||||
"is_query_report": True,
|
||||
},
|
||||
@ -121,6 +82,18 @@ def get_data():
|
||||
{
|
||||
"label": _("Accounting Statements"),
|
||||
"items": [
|
||||
{
|
||||
"type": "report",
|
||||
"name": "Accounts Receivable",
|
||||
"doctype": "Sales Invoice",
|
||||
"is_query_report": True
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "Accounts Payable",
|
||||
"doctype": "Purchase Invoice",
|
||||
"is_query_report": True
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "Trial Balance",
|
||||
@ -224,49 +197,6 @@ def get_data():
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": _("Goods and Services Tax (GST India)"),
|
||||
"items": [
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "GST Settings",
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "GST HSN Code",
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "GSTR-1",
|
||||
"is_query_report": True
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "GSTR-2",
|
||||
"is_query_report": True
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "GST Sales Register",
|
||||
"is_query_report": True
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "GST Purchase Register",
|
||||
"is_query_report": True
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "GST Itemised Sales Register",
|
||||
"is_query_report": True
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "GST Itemised Purchase Register",
|
||||
"is_query_report": True
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": _("Budget and Cost Center"),
|
||||
"items": [
|
||||
@ -290,7 +220,7 @@ def get_data():
|
||||
"doctype": "Cost Center"
|
||||
},
|
||||
{
|
||||
"type":"doctype",
|
||||
"type": "doctype",
|
||||
"name": "Monthly Distribution",
|
||||
"description": _("Seasonality for setting budgets, targets etc.")
|
||||
},
|
||||
@ -347,29 +277,24 @@ def get_data():
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "POS Settings",
|
||||
"description": _("Setup mode of POS (Online / Offline)")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "POS Profile",
|
||||
"label": _("Point-of-Sale Profile"),
|
||||
"description": _("Setup default values for POS Invoices")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name":"Terms and Conditions",
|
||||
"name": "Terms and Conditions",
|
||||
"label": _("Terms and Conditions Template"),
|
||||
"description": _("Template of terms or contract.")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name":"Mode of Payment",
|
||||
"name": "Mode of Payment",
|
||||
"description": _("e.g. Bank, Cash, Credit Card")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name":"C-Form",
|
||||
"name": "Auto Repeat",
|
||||
"label": _("Auto Repeat"),
|
||||
"description": _("To make recurring documents")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "C-Form",
|
||||
"description": _("C-Form records"),
|
||||
"country": "India"
|
||||
}
|
||||
@ -506,12 +431,12 @@ def get_data():
|
||||
"items": [
|
||||
{
|
||||
"type": "doctype",
|
||||
"name":"Shareholder",
|
||||
"name": "Shareholder",
|
||||
"description": _("List of available Shareholders with folio numbers")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name":"Share Transfer",
|
||||
"name": "Share Transfer",
|
||||
"description": _("List of all share transactions"),
|
||||
},
|
||||
{
|
||||
@ -528,28 +453,6 @@ def get_data():
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": _("Subscription Management"),
|
||||
"icon": "fa fa-microchip ",
|
||||
"items": [
|
||||
{
|
||||
"type": "doctype",
|
||||
"name":"Subscriber",
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name":"Subscription Plan",
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name":"Subscription"
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Subscription Settings"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": _("Help"),
|
||||
"icon": "fa fa-facetime-video",
|
||||
@ -572,3 +475,121 @@ def get_data():
|
||||
]
|
||||
}
|
||||
]
|
||||
gst = {
|
||||
"label": _("Goods and Services Tax (GST India)"),
|
||||
"items": [
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "GST Settings",
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "GST HSN Code",
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "GSTR-1",
|
||||
"is_query_report": True
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "GSTR-2",
|
||||
"is_query_report": True
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "GST Sales Register",
|
||||
"is_query_report": True
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "GST Purchase Register",
|
||||
"is_query_report": True
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "GST Itemised Sales Register",
|
||||
"is_query_report": True
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "GST Itemised Purchase Register",
|
||||
"is_query_report": True
|
||||
},
|
||||
]
|
||||
}
|
||||
retail = {
|
||||
"label": _("Retail Operations"),
|
||||
"items": [
|
||||
{
|
||||
"type": "page",
|
||||
"name": "pos",
|
||||
"label": _("POS"),
|
||||
"description": _("Point of Sale")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Cashier Closing",
|
||||
"description": _("Cashier Closing")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "POS Settings",
|
||||
"description": _("Setup mode of POS (Online / Offline)")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "POS Profile",
|
||||
"label": _("Point-of-Sale Profile"),
|
||||
"description": _("Setup default values for POS Invoices")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Loyalty Program",
|
||||
"label": _("Loyalty Program"),
|
||||
"description": _("To make Customer based incentive schemes.")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Loyalty Point Entry",
|
||||
"label": _("Loyalty Point Entry"),
|
||||
"description": _("To view logs of Loyalty Points assigned to a Customer.")
|
||||
}
|
||||
]
|
||||
}
|
||||
subscriptions = {
|
||||
"label": _("Subscription Management"),
|
||||
"icon": "fa fa-microchip ",
|
||||
"items": [
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Subscriber",
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Subscription Plan",
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Subscription"
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Subscription Settings"
|
||||
}
|
||||
]
|
||||
}
|
||||
countries = frappe.get_all("Company", fields="country")
|
||||
countries = [country["country"] for country in countries]
|
||||
if "India" in countries:
|
||||
config.insert(7, gst)
|
||||
domains = frappe.get_active_domains()
|
||||
if "Retail" in domains:
|
||||
config.insert(2, retail)
|
||||
else:
|
||||
config.insert(7, retail)
|
||||
if "Services" in domains:
|
||||
config.insert(2, subscriptions)
|
||||
else:
|
||||
config.insert(7, subscriptions)
|
||||
return config
|
||||
|
@ -57,6 +57,8 @@ class AccountsController(TransactionBase):
|
||||
_('{0} is blocked so this transaction cannot proceed'.format(supplier_name)), raise_exception=1)
|
||||
|
||||
def validate(self):
|
||||
|
||||
self.validate_qty_is_not_zero()
|
||||
if self.get("_action") and self._action != "update_after_submit":
|
||||
self.set_missing_values(for_validate=True)
|
||||
|
||||
@ -179,7 +181,7 @@ class AccountsController(TransactionBase):
|
||||
validate_due_date(self.posting_date, self.due_date,
|
||||
"Customer", self.customer, self.company, self.payment_terms_template)
|
||||
elif self.doctype == "Purchase Invoice":
|
||||
validate_due_date(self.posting_date, self.due_date,
|
||||
validate_due_date(self.bill_date or self.posting_date, self.due_date,
|
||||
"Supplier", self.supplier, self.company, self.bill_date, self.payment_terms_template)
|
||||
|
||||
def set_price_list_currency(self, buying_or_selling):
|
||||
@ -359,6 +361,11 @@ class AccountsController(TransactionBase):
|
||||
|
||||
return gl_dict
|
||||
|
||||
def validate_qty_is_not_zero(self):
|
||||
for item in self.items:
|
||||
if not item.qty:
|
||||
frappe.throw("Item quantity can not be zero")
|
||||
|
||||
def validate_account_currency(self, account, account_currency=None):
|
||||
valid_currency = [self.company_currency]
|
||||
if self.get("currency") and self.currency != self.company_currency:
|
||||
@ -405,7 +412,8 @@ class AccountsController(TransactionBase):
|
||||
if d.against_order:
|
||||
allocated_amount = flt(d.amount)
|
||||
else:
|
||||
allocated_amount = min(self.grand_total - advance_allocated, d.amount)
|
||||
amount = self.rounded_total or self.grand_total
|
||||
allocated_amount = min(amount - advance_allocated, d.amount)
|
||||
advance_allocated += flt(allocated_amount)
|
||||
|
||||
self.append("advances", {
|
||||
|
@ -12,6 +12,7 @@ from erpnext.buying.utils import validate_for_items, update_last_purchase_rate
|
||||
from erpnext.stock.stock_ledger import get_valuation_rate
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry import get_used_alternative_items
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_auto_serial_nos, auto_make_serial_nos, get_serial_nos
|
||||
from frappe.contacts.doctype.address.address import get_address_display
|
||||
|
||||
from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
|
||||
from erpnext.controllers.stock_controller import StockController
|
||||
@ -42,6 +43,7 @@ class BuyingController(StockController):
|
||||
self.set_qty_as_per_stock_uom()
|
||||
self.validate_stock_or_nonstock_items()
|
||||
self.validate_warehouse()
|
||||
self.set_supplier_address()
|
||||
|
||||
if self.doctype=="Purchase Invoice":
|
||||
self.validate_purchase_receipt_if_update_stock()
|
||||
@ -113,6 +115,16 @@ class BuyingController(StockController):
|
||||
if not d.cost_center and lc_voucher_data and lc_voucher_data[0][1]:
|
||||
d.db_set('cost_center', lc_voucher_data[0][1])
|
||||
|
||||
def set_supplier_address(self):
|
||||
address_dict = {
|
||||
'supplier_address': 'address_display',
|
||||
'shipping_address': 'shipping_address_display'
|
||||
}
|
||||
|
||||
for address_field, address_display_field in address_dict.items():
|
||||
if self.get(address_field):
|
||||
self.set(address_display_field, get_address_display(self.get(address_field)))
|
||||
|
||||
def set_total_in_words(self):
|
||||
from frappe.utils import money_in_words
|
||||
if self.meta.get_field("base_in_words"):
|
||||
|
@ -9,6 +9,7 @@ from erpnext.stock.get_item_details import get_bin_details
|
||||
from erpnext.stock.utils import get_incoming_rate
|
||||
from erpnext.stock.get_item_details import get_conversion_factor
|
||||
from erpnext.stock.doctype.item.item import get_item_defaults, set_item_default
|
||||
from frappe.contacts.doctype.address.address import get_address_display
|
||||
|
||||
from erpnext.controllers.stock_controller import StockController
|
||||
|
||||
@ -16,7 +17,7 @@ class SellingController(StockController):
|
||||
def __setup__(self):
|
||||
if hasattr(self, "taxes"):
|
||||
self.flags.print_taxes_with_zero_amount = cint(frappe.db.get_single_value("Print Settings",
|
||||
"print_taxes_with_zero_amount"))
|
||||
"print_taxes_with_zero_amount"))
|
||||
self.flags.show_inclusive_tax_in_print = self.is_inclusive_tax()
|
||||
|
||||
self.print_templates = {
|
||||
@ -41,7 +42,9 @@ class SellingController(StockController):
|
||||
self.validate_selling_price()
|
||||
self.set_qty_as_per_stock_uom()
|
||||
self.set_po_nos()
|
||||
self.set_gross_profit()
|
||||
set_default_income_account_for_item(self)
|
||||
self.set_customer_address()
|
||||
|
||||
def set_missing_values(self, for_validate=False):
|
||||
|
||||
@ -348,6 +351,23 @@ class SellingController(StockController):
|
||||
if po_nos and po_nos[0].get('po_no'):
|
||||
self.po_no = ', '.join(list(set([d.po_no for d in po_nos if d.po_no])))
|
||||
|
||||
def set_gross_profit(self):
|
||||
if self.doctype == "Sales Order":
|
||||
for item in self.items:
|
||||
item.gross_profit = flt(((item.base_rate - item.valuation_rate) * item.stock_qty), self.precision("amount", item))
|
||||
|
||||
|
||||
def set_customer_address(self):
|
||||
address_dict = {
|
||||
'customer_address': 'address_display',
|
||||
'shipping_address_name': 'shipping_address',
|
||||
'company_address': 'company_address_display'
|
||||
}
|
||||
|
||||
for address_field, address_display_field in address_dict.items():
|
||||
if self.get(address_field):
|
||||
self.set(address_display_field, get_address_display(self.get(address_field)))
|
||||
|
||||
def validate_items(self):
|
||||
# validate items to see if they have is_sales_item enabled
|
||||
from erpnext.controllers.buying_controller import validate_item_type
|
||||
|
@ -65,6 +65,7 @@ class calculate_taxes_and_totals(object):
|
||||
|
||||
if item.doctype in ['Quotation Item', 'Sales Order Item', 'Delivery Note Item', 'Sales Invoice Item']:
|
||||
item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
|
||||
|
||||
if flt(item.rate_with_margin) > 0:
|
||||
item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
|
||||
item.discount_amount = item.rate_with_margin - item.rate
|
||||
@ -647,4 +648,4 @@ def get_rounded_tax_amount(itemised_tax, precision):
|
||||
# Rounding based on tax_amount precision
|
||||
for taxes in itemised_tax.values():
|
||||
for tax_account in taxes:
|
||||
taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
|
||||
taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
|
||||
|
@ -376,7 +376,7 @@ def setup_pos_profile():
|
||||
company_abbr = frappe.get_cached_value('Company', erpnext.get_default_company(), "abbr")
|
||||
pos = frappe.new_doc('POS Profile')
|
||||
pos.user = frappe.db.get_global('demo_accounts_user')
|
||||
pos.pos_profile_name = "Demo POS Profile"
|
||||
pos.name = "Demo POS Profile"
|
||||
pos.naming_series = 'SINV-'
|
||||
pos.update_stock = 0
|
||||
pos.write_off_account = 'Cost of Goods Sold - '+ company_abbr
|
||||
|
@ -38,6 +38,12 @@ frappe.ui.form.on("Assessment Result", {
|
||||
frappe.ui.form.on("Assessment Result Detail", {
|
||||
score: function(frm, cdt, cdn) {
|
||||
var d = locals[cdt][cdn];
|
||||
|
||||
if(!d.maximum_score || !frm.doc.grading_scale) {
|
||||
d.score = "";
|
||||
frappe.throw(__("Please fill in all the details to generate Assessment Result."));
|
||||
}
|
||||
|
||||
if (d.score > d.maximum_score) {
|
||||
frappe.throw(__("Score cannot be greater than Maximum Score"));
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 0,
|
||||
@ -991,39 +992,6 @@
|
||||
"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": "paid_amount",
|
||||
"fieldtype": "Currency",
|
||||
"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": "Paid Amount",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"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,
|
||||
@ -1360,7 +1328,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2018-08-21 14:44:48.968839",
|
||||
"modified": "2018-11-26 20:42:14.467284",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Education",
|
||||
"name": "Fees",
|
||||
|
@ -112,7 +112,10 @@ def get_fee_list(doctype, txt, filters, limit_start, limit_page_length=20, order
|
||||
user = frappe.session.user
|
||||
student = frappe.db.sql("select name from `tabStudent` where student_email_id= %s", user)
|
||||
if student:
|
||||
return frappe. db.sql('''select name, program, due_date, paid_amount, outstanding_amount, grand_total from `tabFees`
|
||||
return frappe. db.sql('''
|
||||
select name, program, due_date, grand_total - outstanding_amount as paid_amount,
|
||||
outstanding_amount, grand_total, currency
|
||||
from `tabFees`
|
||||
where student= %s and docstatus=1
|
||||
order by due_date asc limit {0} , {1}'''
|
||||
.format(limit_start, limit_page_length), student, as_dict = True)
|
||||
|
@ -1,18 +1,18 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2016-06-22 02:58:41.024538",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 3,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2017-11-10 19:41:37.320224",
|
||||
"modified": "2018-12-17 16:46:46.176620",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Education",
|
||||
"name": "Student Fee Collection",
|
||||
"owner": "Administrator",
|
||||
"query": "SELECT\n student as \"Student:Link/Student:200\",\n student_name as \"Student Name::200\",\n sum(paid_amount) as \"Paid Amount:Currency:150\",\n sum(outstanding_amount) as \"Outstanding Amount:Currency:150\",\n sum(grand_total) as \"Grand Total:Currency:150\"\nFROM\n `tabFees` \nGROUP BY\n student",
|
||||
"prepared_report": 0,
|
||||
"query": "SELECT\n student as \"Student:Link/Student:200\",\n student_name as \"Student Name::200\",\n sum(grand_total) - sum(outstanding_amount) as \"Paid Amount:Currency:150\",\n sum(outstanding_amount) as \"Outstanding Amount:Currency:150\",\n sum(grand_total) as \"Grand Total:Currency:150\"\nFROM\n `tabFees` \nGROUP BY\n student",
|
||||
"ref_doctype": "Fees",
|
||||
"report_name": "Student Fee Collection",
|
||||
"report_type": "Query Report",
|
||||
|
@ -120,3 +120,16 @@ var btn_invoice_registration = function (frm) {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
frappe.ui.form.on('Patient Relation', {
|
||||
patient_relation_add: function(frm){
|
||||
frm.fields_dict['patient_relation'].grid.get_field('patient').get_query = function(frm){
|
||||
var patient_list = [];
|
||||
if(!frm.doc.__islocal) patient_list.push(frm.doc.name);
|
||||
$.each(frm.doc.patient_relation, function(idx, val){
|
||||
if (val.patient) patient_list.push(val.patient);
|
||||
});
|
||||
return { filters: [['Patient', 'name', 'not in', patient_list]] };
|
||||
};
|
||||
}
|
||||
});
|
@ -39,7 +39,9 @@ frappe.ui.form.on('Patient Appointment', {
|
||||
frm.add_custom_button(__('Cancel'), function() {
|
||||
btn_update_status(frm, "Cancelled");
|
||||
});
|
||||
|
||||
frm.add_custom_button(__('Reschedule'), function() {
|
||||
check_and_set_availability(frm);
|
||||
});
|
||||
if(frm.doc.procedure_template){
|
||||
frm.add_custom_button(__("Procedure"),function(){
|
||||
btn_create_procedure(frm);
|
||||
@ -59,7 +61,9 @@ frappe.ui.form.on('Patient Appointment', {
|
||||
frm.add_custom_button(__('Cancel'), function() {
|
||||
btn_update_status(frm, "Cancelled");
|
||||
});
|
||||
|
||||
frm.add_custom_button(__('Reschedule'), function() {
|
||||
check_and_set_availability(frm);
|
||||
});
|
||||
if(frm.doc.procedure_template){
|
||||
frm.add_custom_button(__("Procedure"),function(){
|
||||
btn_create_procedure(frm);
|
||||
@ -100,117 +104,7 @@ frappe.ui.form.on('Patient Appointment', {
|
||||
});
|
||||
},
|
||||
check_availability: function(frm) {
|
||||
var { practitioner, appointment_date } = frm.doc;
|
||||
if(!(practitioner && appointment_date)) {
|
||||
frappe.throw(__("Please select Healthcare Practitioner and Date"));
|
||||
}
|
||||
|
||||
// show booking modal
|
||||
frm.call({
|
||||
method: 'get_availability_data',
|
||||
args: {
|
||||
practitioner: practitioner,
|
||||
date: appointment_date
|
||||
},
|
||||
callback: (r) => {
|
||||
var data = r.message;
|
||||
if(data.slot_details.length > 0){
|
||||
show_availability(data);
|
||||
}else{
|
||||
show_empty_state();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function show_empty_state() {
|
||||
frappe.msgprint({
|
||||
title: __('Not Available'),
|
||||
message: __("Healthcare Practitioner {0} not available on {1}", [practitioner.bold(), appointment_date.bold()]),
|
||||
indicator: 'red'
|
||||
});
|
||||
}
|
||||
|
||||
function show_availability(data) {
|
||||
var d = new frappe.ui.Dialog({
|
||||
title: __("Available slots"),
|
||||
fields: [{ fieldtype: 'HTML', fieldname: 'available_slots'}],
|
||||
primary_action_label: __("Book"),
|
||||
primary_action: function() {
|
||||
// book slot
|
||||
var btn_selected = $wrapper.find('button.btn-selected-slot');
|
||||
frm.set_value('appointment_time', btn_selected.attr('data-name'));
|
||||
frm.set_value('service_unit', btn_selected.attr('data-service-unit') || '');
|
||||
frm.set_value('duration', btn_selected.attr('data-duration'));
|
||||
d.hide();
|
||||
frm.enable_save();
|
||||
frm.save();
|
||||
frm.enable_save();
|
||||
}
|
||||
});
|
||||
var $wrapper = d.fields_dict.available_slots.$wrapper;
|
||||
|
||||
// disable dialog action initially
|
||||
d.get_primary_btn().attr('disabled', true);
|
||||
|
||||
var slot_details = data.slot_details;
|
||||
var slot_html = "";
|
||||
var duration = frm.doc.duration | 0;
|
||||
$.each(slot_details, function(i, slot_detail){
|
||||
slot_html = slot_html + `<label>${slot_detail['slot_name']}</label>`;
|
||||
slot_html = slot_html + `<br/>` + slot_detail['avail_slot'].map(slot => {
|
||||
let disabled = '';
|
||||
let start_str = slot.from_time;
|
||||
let slot_start_time = moment(slot.from_time, 'HH:mm:ss');
|
||||
let slot_to_time = moment(slot.to_time, 'HH:mm:ss');
|
||||
let interval = (slot_to_time - slot_start_time)/60000 | 0;
|
||||
// iterate in all booked appointments, update the start time and duration
|
||||
slot_detail['appointments'].forEach(function(booked) {
|
||||
let booked_moment = moment(booked.appointment_time, 'HH:mm:ss');
|
||||
let end_time = booked_moment.clone().add(booked.duration, 'minutes');
|
||||
// Deal with 0 duration appointments
|
||||
if(booked_moment.isSame(slot_start_time) || booked_moment.isBetween(slot_start_time, slot_to_time)){
|
||||
if(booked.duration == 0){
|
||||
disabled = 'disabled="disabled"';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Check for overlaps considering appointment duration
|
||||
if(slot_start_time.isBefore(end_time) && slot_to_time.isAfter(booked_moment)){
|
||||
// There is an overlap
|
||||
disabled = 'disabled="disabled"';
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return `<button class="btn btn-default"
|
||||
data-name=${start_str}
|
||||
data-duration=${duration || interval}
|
||||
data-service-unit="${slot_detail['service_unit'] || ''}"
|
||||
style="margin: 0 10px 10px 0; width: 72px;" ${disabled}>
|
||||
${start_str.substring(0, start_str.length - 3)}
|
||||
</button>`;
|
||||
}).join("");
|
||||
slot_html = slot_html + `<br/>`;
|
||||
});
|
||||
|
||||
$wrapper
|
||||
.css('margin-bottom', 0)
|
||||
.addClass('text-center')
|
||||
.html(slot_html);
|
||||
|
||||
// blue button when clicked
|
||||
$wrapper.on('click', 'button', function() {
|
||||
var $btn = $(this);
|
||||
$wrapper.find('button').removeClass('btn-primary');
|
||||
$wrapper.find('button').removeClass('btn-selected-slot');
|
||||
$btn.addClass('btn-primary');
|
||||
$btn.addClass('btn-selected-slot');
|
||||
// enable dialog action
|
||||
d.get_primary_btn().attr('disabled', null);
|
||||
});
|
||||
|
||||
d.show();
|
||||
}
|
||||
check_and_set_availability(frm);
|
||||
},
|
||||
onload:function(frm){
|
||||
if(frm.is_new()) {
|
||||
@ -223,6 +117,176 @@ frappe.ui.form.on('Patient Appointment', {
|
||||
}
|
||||
});
|
||||
|
||||
var check_and_set_availability = function(frm) {
|
||||
var selected_slot = null;
|
||||
var service_unit = null;
|
||||
var duration = null;
|
||||
|
||||
show_availability();
|
||||
|
||||
function show_empty_state(practitioner, appointment_date) {
|
||||
frappe.msgprint({
|
||||
title: __('Not Available'),
|
||||
message: __("Healthcare Practitioner {0} not available on {1}", [practitioner.bold(), appointment_date.bold()]),
|
||||
indicator: 'red'
|
||||
});
|
||||
}
|
||||
|
||||
function show_availability() {
|
||||
let selected_practitioner = '';
|
||||
var d = new frappe.ui.Dialog({
|
||||
title: __("Available slots"),
|
||||
fields: [
|
||||
{ fieldtype: 'Link', options: 'Medical Department', reqd:1, fieldname: 'department', label: 'Medical Department'},
|
||||
{ fieldtype: 'Column Break'},
|
||||
{ fieldtype: 'Link', options: 'Healthcare Practitioner', reqd:1, fieldname: 'practitioner', label: 'Healthcare Practitioner'},
|
||||
{ fieldtype: 'Column Break'},
|
||||
{ fieldtype: 'Date', reqd:1, fieldname: 'appointment_date', label: 'Date'},
|
||||
{ fieldtype: 'Section Break'},
|
||||
{ fieldtype: 'HTML', fieldname: 'available_slots'}
|
||||
],
|
||||
primary_action_label: __("Book"),
|
||||
primary_action: function() {
|
||||
frm.set_value('appointment_time', selected_slot);
|
||||
frm.set_value('service_unit', service_unit || '');
|
||||
frm.set_value('duration', duration);
|
||||
frm.set_value('practitioner', d.get_value('practitioner'));
|
||||
frm.set_value('department', d.get_value('department'));
|
||||
frm.set_value('appointment_date', d.get_value('appointment_date'));
|
||||
d.hide();
|
||||
frm.enable_save();
|
||||
frm.save();
|
||||
frm.enable_save();
|
||||
d.get_primary_btn().attr('disabled', true);
|
||||
}
|
||||
});
|
||||
|
||||
d.set_values({
|
||||
'department': frm.doc.department,
|
||||
'practitioner': frm.doc.practitioner,
|
||||
'appointment_date': frm.doc.appointment_date
|
||||
});
|
||||
|
||||
d.fields_dict["department"].df.onchange = () => {
|
||||
d.set_values({
|
||||
'practitioner': ''
|
||||
});
|
||||
var department = d.get_value('department');
|
||||
if(department){
|
||||
d.fields_dict.practitioner.get_query = function() {
|
||||
return {
|
||||
filters: {
|
||||
"department": department
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// disable dialog action initially
|
||||
d.get_primary_btn().attr('disabled', true);
|
||||
|
||||
// Field Change Handler
|
||||
|
||||
var fd = d.fields_dict;
|
||||
|
||||
d.fields_dict["appointment_date"].df.onchange = () => {
|
||||
show_slots(d, fd);
|
||||
};
|
||||
d.fields_dict["practitioner"].df.onchange = () => {
|
||||
if(d.get_value('practitioner') && d.get_value('practitioner') != selected_practitioner){
|
||||
selected_practitioner = d.get_value('practitioner');
|
||||
show_slots(d, fd);
|
||||
}
|
||||
};
|
||||
d.show();
|
||||
}
|
||||
|
||||
function show_slots(d, fd) {
|
||||
if (d.get_value('appointment_date') && d.get_value('practitioner')){
|
||||
fd.available_slots.html("");
|
||||
frappe.call({
|
||||
method: 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.get_availability_data',
|
||||
args: {
|
||||
practitioner: d.get_value('practitioner'),
|
||||
date: d.get_value('appointment_date')
|
||||
},
|
||||
callback: (r) => {
|
||||
var data = r.message;
|
||||
if(data.slot_details.length > 0) {
|
||||
var $wrapper = d.fields_dict.available_slots.$wrapper;
|
||||
|
||||
// make buttons for each slot
|
||||
var slot_details = data.slot_details;
|
||||
var slot_html = "";
|
||||
for (let i = 0; i < slot_details.length; i++) {
|
||||
slot_html = slot_html + `<label>${slot_details[i].slot_name}</label>`;
|
||||
slot_html = slot_html + `<br/>` + slot_details[i].avail_slot.map(slot => {
|
||||
let disabled = '';
|
||||
let start_str = slot.from_time;
|
||||
let slot_start_time = moment(slot.from_time, 'HH:mm:ss');
|
||||
let slot_to_time = moment(slot.to_time, 'HH:mm:ss');
|
||||
let interval = (slot_to_time - slot_start_time)/60000 | 0;
|
||||
// iterate in all booked appointments, update the start time and duration
|
||||
slot_details[i].appointments.forEach(function(booked) {
|
||||
let booked_moment = moment(booked.appointment_time, 'HH:mm:ss');
|
||||
let end_time = booked_moment.clone().add(booked.duration, 'minutes');
|
||||
// Deal with 0 duration appointments
|
||||
if(booked_moment.isSame(slot_start_time) || booked_moment.isBetween(slot_start_time, slot_to_time)){
|
||||
if(booked.duration == 0){
|
||||
disabled = 'disabled="disabled"';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Check for overlaps considering appointment duration
|
||||
if(slot_start_time.isBefore(end_time) && slot_to_time.isAfter(booked_moment)){
|
||||
// There is an overlap
|
||||
disabled = 'disabled="disabled"';
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return `<button class="btn btn-default"
|
||||
data-name=${start_str}
|
||||
data-duration=${interval}
|
||||
data-service-unit="${slot_details[i].service_unit || ''}"
|
||||
style="margin: 0 10px 10px 0; width: 72px;" ${disabled}>
|
||||
${start_str.substring(0, start_str.length - 3)}
|
||||
</button>`;
|
||||
}).join("");
|
||||
slot_html = slot_html + `<br/>`;
|
||||
}
|
||||
|
||||
$wrapper
|
||||
.css('margin-bottom', 0)
|
||||
.addClass('text-center')
|
||||
.html(slot_html);
|
||||
|
||||
// blue button when clicked
|
||||
$wrapper.on('click', 'button', function() {
|
||||
var $btn = $(this);
|
||||
$wrapper.find('button').removeClass('btn-primary');
|
||||
$btn.addClass('btn-primary');
|
||||
selected_slot = $btn.attr('data-name');
|
||||
service_unit = $btn.attr('data-service-unit');
|
||||
duration = $btn.attr('data-duration');
|
||||
// enable dialog action
|
||||
d.get_primary_btn().attr('disabled', null);
|
||||
});
|
||||
|
||||
}else {
|
||||
// fd.available_slots.html("Please select a valid date.".bold())
|
||||
show_empty_state(d.get_value('practitioner'), d.get_value('appointment_date'));
|
||||
}
|
||||
},
|
||||
freeze: true,
|
||||
freeze_message: __("Fetching records......")
|
||||
});
|
||||
}else{
|
||||
fd.available_slots.html("Appointment date and Healthcare Practitioner are Mandatory".bold());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var get_procedure_prescribed = function(frm){
|
||||
if(frm.doc.patient){
|
||||
frappe.call({
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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.24'
|
||||
staging_version = '11.0.3-beta.30'
|
||||
|
||||
error_report_email = "support@erpnext.com"
|
||||
|
||||
|
@ -36,10 +36,13 @@ class EmployeeBenefitClaim(Document):
|
||||
frappe.throw(_("Maximum benefit amount of employee {0} exceeds {1}").format(self.employee, max_benefits))
|
||||
|
||||
def validate_max_benefit_for_component(self, payroll_period):
|
||||
claimed_amount = self.claimed_amount
|
||||
claimed_amount += get_previous_claimed_amount(self.employee, payroll_period, component = self.earning_component)
|
||||
if claimed_amount > self.max_amount_eligible:
|
||||
frappe.throw(_("Maximum amount eligible for the component {0} exceeds {1}").format(self.earning_component, self.max_amount_eligible))
|
||||
if self.max_amount_eligible:
|
||||
claimed_amount = self.claimed_amount
|
||||
claimed_amount += get_previous_claimed_amount(self.employee,
|
||||
payroll_period, component = self.earning_component)
|
||||
if claimed_amount > self.max_amount_eligible:
|
||||
frappe.throw(_("Maximum amount eligible for the component {0} exceeds {1}")
|
||||
.format(self.earning_component, self.max_amount_eligible))
|
||||
|
||||
def validate_non_pro_rata_benefit_claim(self, max_benefits, payroll_period):
|
||||
claimed_amount = self.claimed_amount
|
||||
@ -75,12 +78,17 @@ def get_benefit_pro_rata_ratio_amount(employee, on_date, sal_struct):
|
||||
total_pro_rata_max = 0
|
||||
benefit_amount_total = 0
|
||||
for sal_struct_row in sal_struct.get("earnings"):
|
||||
pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value("Salary Component", sal_struct_row.salary_component, ["pay_against_benefit_claim", "max_benefit_amount"])
|
||||
try:
|
||||
pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value("Salary Component", sal_struct_row.salary_component, ["pay_against_benefit_claim", "max_benefit_amount"])
|
||||
except TypeError:
|
||||
# show the error in tests?
|
||||
frappe.throw("Unable to find Salary Component {0}".format(sal_struct_row.salary_component))
|
||||
if sal_struct_row.is_flexible_benefit == 1 and pay_against_benefit_claim != 1:
|
||||
total_pro_rata_max += max_benefit_amount
|
||||
if total_pro_rata_max > 0:
|
||||
for sal_struct_row in sal_struct.get("earnings"):
|
||||
pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value("Salary Component", sal_struct_row.salary_component, ["pay_against_benefit_claim", "max_benefit_amount"])
|
||||
|
||||
if sal_struct_row.is_flexible_benefit == 1 and pay_against_benefit_claim != 1:
|
||||
component_max = max_benefit_amount
|
||||
benefit_amount = component_max * sal_struct.max_benefits / total_pro_rata_max
|
||||
|
@ -1,5 +1,5 @@
|
||||
frappe.listview_settings['Employee Separation'] = {
|
||||
add_fields: ["boarding_status", "employee_name", "date_of_joining", "department"],
|
||||
add_fields: ["boarding_status", "employee_name", "department"],
|
||||
filters:[["boarding_status","=", "Pending"]],
|
||||
get_indicator: function(doc) {
|
||||
return [__(doc.boarding_status), frappe.utils.guess_colour(doc.boarding_status), "status,=," + doc.boarding_status];
|
||||
|
@ -3,7 +3,9 @@
|
||||
|
||||
frappe.ui.form.on('Holiday List', {
|
||||
refresh: function(frm) {
|
||||
frm.set_value('total_holidays', frm.doc.holidays.length);
|
||||
if (frm.doc.holidays) {
|
||||
frm.set_value('total_holidays', frm.doc.holidays.length);
|
||||
}
|
||||
},
|
||||
from_date: function(frm) {
|
||||
if (frm.doc.from_date && !frm.doc.to_date) {
|
||||
|
@ -14,7 +14,7 @@ frappe.ui.form.on("Job Offer", {
|
||||
|
||||
refresh: function (frm) {
|
||||
if ((!frm.doc.__islocal) && (frm.doc.status == 'Accepted')
|
||||
&& (frm.doc.docstatus === 1) && (!frm.doc.__onload.employee)) {
|
||||
&& (frm.doc.docstatus === 1) && (!frm.doc.__onload || !frm.doc.__onload.employee)) {
|
||||
frm.add_custom_button(__('Make Employee'),
|
||||
function () {
|
||||
erpnext.job_offer.make_employee(frm);
|
||||
@ -22,7 +22,7 @@ frappe.ui.form.on("Job Offer", {
|
||||
);
|
||||
}
|
||||
|
||||
if(frm.doc.__onload.employee) {
|
||||
if(frm.doc.__onload && frm.doc.__onload.employee) {
|
||||
frm.add_custom_button(__('Show Employee'),
|
||||
function () {
|
||||
frappe.set_route("Form", "Employee", frm.doc.__onload.employee);
|
||||
|
@ -187,7 +187,7 @@ class LeaveApplication(Document):
|
||||
self.total_leave_days = get_number_of_leave_days(self.employee, self.leave_type,
|
||||
self.from_date, self.to_date, self.half_day, self.half_day_date)
|
||||
|
||||
if self.total_leave_days == 0:
|
||||
if self.total_leave_days <= 0:
|
||||
frappe.throw(_("The day(s) on which you are applying for leave are holidays. You need not apply for leave."))
|
||||
|
||||
if not is_lwp(self.leave_type):
|
||||
|
@ -1,3 +1,10 @@
|
||||
frappe.listview_settings['Leave Application'] = {
|
||||
add_fields: ["leave_type", "employee", "employee_name", "total_leave_days", "from_date", "to_date"]
|
||||
add_fields: ["leave_type", "employee", "employee_name", "total_leave_days", "from_date", "to_date"],
|
||||
get_indicator: function (doc) {
|
||||
if (doc.status === "Approved") {
|
||||
return [__("Approved"), "green", "status,=,Approved"];
|
||||
} else if (doc.status === "Rejected") {
|
||||
return [__("Rejected"), "red", "status,=,Rejected"];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -177,7 +177,7 @@ var calculate_earning_total = function(doc, dt, dn, reset_amount) {
|
||||
if(cint(tbl[i].depends_on_lwp) == 1) {
|
||||
tbl[i].amount = Math.round(tbl[i].default_amount)*(flt(doc.payment_days) /
|
||||
cint(doc.total_working_days)*100)/100;
|
||||
} else if(reset_amount) {
|
||||
} else if(reset_amount && tbl[i].default_amount) {
|
||||
tbl[i].amount = tbl[i].default_amount;
|
||||
}
|
||||
if(!tbl[i].do_not_include_in_total) {
|
||||
@ -198,7 +198,7 @@ var calculate_ded_total = function(doc, dt, dn, reset_amount) {
|
||||
for(var i = 0; i < tbl.length; i++){
|
||||
if(cint(tbl[i].depends_on_lwp) == 1) {
|
||||
tbl[i].amount = Math.round(tbl[i].default_amount)*(flt(doc.payment_days)/cint(doc.total_working_days)*100)/100;
|
||||
} else if(reset_amount) {
|
||||
} else if(reset_amount && tbl[i].default_amount) {
|
||||
tbl[i].amount = tbl[i].default_amount;
|
||||
}
|
||||
if(!tbl[i].do_not_include_in_total) {
|
||||
|
@ -26,6 +26,7 @@ class TestSalarySlip(unittest.TestCase):
|
||||
self.make_holiday_list()
|
||||
|
||||
frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Slip Test Holiday List")
|
||||
frappe.db.set_value("HR Settings", None, "email_salary_slip_to_employee", 0)
|
||||
|
||||
def tearDown(self):
|
||||
frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0)
|
||||
@ -255,6 +256,9 @@ class TestSalarySlip(unittest.TestCase):
|
||||
raise
|
||||
frappe.db.sql("""delete from `tabAdditional Salary` where employee=%s""", (employee))
|
||||
|
||||
# undelete fixture data
|
||||
frappe.db.rollback()
|
||||
|
||||
def make_holiday_list(self):
|
||||
fiscal_year = get_fiscal_year(nowdate(), company=erpnext.get_default_company())
|
||||
if not frappe.db.get_value("Holiday List", "Salary Slip Test Holiday List"):
|
||||
|
@ -10,7 +10,8 @@
|
||||
"rate": 5000.0,
|
||||
"uom": "_Test UOM",
|
||||
"stock_uom": "_Test UOM",
|
||||
"source_warehouse": "_Test Warehouse - _TC"
|
||||
"source_warehouse": "_Test Warehouse - _TC",
|
||||
"allow_transfer_for_manufacture": 1
|
||||
},
|
||||
{
|
||||
"amount": 2000.0,
|
||||
@ -21,7 +22,8 @@
|
||||
"rate": 1000.0,
|
||||
"uom": "_Test UOM",
|
||||
"stock_uom": "_Test UOM",
|
||||
"source_warehouse": "_Test Warehouse - _TC"
|
||||
"source_warehouse": "_Test Warehouse - _TC",
|
||||
"allow_transfer_for_manufacture": 1
|
||||
}
|
||||
],
|
||||
"docstatus": 1,
|
||||
@ -54,7 +56,8 @@
|
||||
"rate": 5000.0,
|
||||
"uom": "_Test UOM",
|
||||
"stock_uom": "_Test UOM",
|
||||
"source_warehouse": "_Test Warehouse - _TC"
|
||||
"source_warehouse": "_Test Warehouse - _TC",
|
||||
"allow_transfer_for_manufacture": 1
|
||||
},
|
||||
{
|
||||
"amount": 2000.0,
|
||||
@ -65,7 +68,8 @@
|
||||
"rate": 1000.0,
|
||||
"uom": "_Test UOM",
|
||||
"stock_uom": "_Test UOM",
|
||||
"source_warehouse": "_Test Warehouse - _TC"
|
||||
"source_warehouse": "_Test Warehouse - _TC",
|
||||
"allow_transfer_for_manufacture": 1
|
||||
}
|
||||
],
|
||||
"docstatus": 1,
|
||||
@ -97,7 +101,8 @@
|
||||
"rate": 5000.0,
|
||||
"uom": "_Test UOM",
|
||||
"stock_uom": "_Test UOM",
|
||||
"source_warehouse": "_Test Warehouse - _TC"
|
||||
"source_warehouse": "_Test Warehouse - _TC",
|
||||
"allow_transfer_for_manufacture": 1
|
||||
},
|
||||
{
|
||||
"amount": 3000.0,
|
||||
@ -109,7 +114,8 @@
|
||||
"rate": 1000.0,
|
||||
"uom": "_Test UOM",
|
||||
"stock_uom": "_Test UOM",
|
||||
"source_warehouse": "_Test Warehouse - _TC"
|
||||
"source_warehouse": "_Test Warehouse - _TC",
|
||||
"allow_transfer_for_manufacture": 1
|
||||
}
|
||||
],
|
||||
"docstatus": 1,
|
||||
@ -143,7 +149,8 @@
|
||||
"rate": 3000.0,
|
||||
"uom": "_Test UOM",
|
||||
"stock_uom": "_Test UOM",
|
||||
"source_warehouse": "_Test Warehouse - _TC"
|
||||
"source_warehouse": "_Test Warehouse - _TC",
|
||||
"allow_transfer_for_manufacture": 1
|
||||
}
|
||||
],
|
||||
"docstatus": 1,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -110,7 +110,7 @@ class JobCard(Document):
|
||||
|
||||
if self.items:
|
||||
self.transferred_qty = frappe.db.get_value('Stock Entry', {'job_card': self.name,
|
||||
'work_order': self.work_order, 'docstatus': 1}, 'sum(fg_completed_qty)')
|
||||
'work_order': self.work_order, 'docstatus': 1}, 'sum(fg_completed_qty)') or 0
|
||||
|
||||
self.db_set("transferred_qty", self.transferred_qty)
|
||||
|
||||
|
@ -13,3 +13,11 @@ class ManufacturingSettings(Document):
|
||||
def get_mins_between_operations():
|
||||
return relativedelta(minutes=cint(frappe.db.get_single_value("Manufacturing Settings",
|
||||
"mins_between_operations")) or 10)
|
||||
|
||||
@frappe.whitelist()
|
||||
def is_material_consumption_enabled():
|
||||
if not hasattr(frappe.local, 'material_consumption'):
|
||||
frappe.local.material_consumption = cint(frappe.db.get_single_value('Manufacturing Settings',
|
||||
'material_consumption'))
|
||||
|
||||
return frappe.local.material_consumption
|
@ -514,7 +514,7 @@ def get_items_for_material_requests(doc, company=None):
|
||||
doc = frappe._dict(json.loads(doc))
|
||||
|
||||
doc['mr_items'] = []
|
||||
po_items = doc['po_items'] if doc.get('po_items') else doc['items']
|
||||
po_items = doc.get('po_items') if doc.get('po_items') else doc.get('items')
|
||||
|
||||
for data in po_items:
|
||||
warehouse = None
|
||||
@ -533,10 +533,10 @@ def get_items_for_material_requests(doc, company=None):
|
||||
else:
|
||||
planned_qty = data.get('planned_qty')
|
||||
bom_no = data.get('bom_no')
|
||||
include_subcontracted_items = doc['include_subcontracted_items']
|
||||
company = doc['company']
|
||||
include_non_stock_items = doc['include_non_stock_items']
|
||||
ignore_existing_ordered_qty = doc['ignore_existing_ordered_qty']
|
||||
include_subcontracted_items = doc.get('include_subcontracted_items')
|
||||
company = doc.get('company')
|
||||
include_non_stock_items = doc.get('include_non_stock_items')
|
||||
ignore_existing_ordered_qty = doc.get('ignore_existing_ordered_qty')
|
||||
if not planned_qty:
|
||||
frappe.throw(_("For row {0}: Enter Planned Qty").format(data.get('idx')))
|
||||
|
||||
|
@ -48,7 +48,7 @@ class TestProductionPlan(unittest.TestCase):
|
||||
filters = {'production_plan': pln.name}, as_list=1)
|
||||
|
||||
self.assertTrue(len(work_orders), len(pln.po_items))
|
||||
|
||||
|
||||
for name in material_requests:
|
||||
mr = frappe.get_doc('Material Request', name[0])
|
||||
mr.cancel()
|
||||
@ -164,7 +164,7 @@ def create_production_plan(**args):
|
||||
mr_items = get_items_for_material_requests(pln.as_dict())
|
||||
for d in mr_items:
|
||||
pln.append('mr_items', d)
|
||||
|
||||
|
||||
if not args.do_not_save:
|
||||
pln.insert()
|
||||
if not args.do_not_submit:
|
||||
@ -182,7 +182,7 @@ def make_bom(**args):
|
||||
'quantity': args.quantity or 1,
|
||||
'company': args.company or '_Test Company'
|
||||
})
|
||||
|
||||
|
||||
for item in args.raw_materials:
|
||||
item_doc = frappe.get_doc('Item', item)
|
||||
|
||||
@ -191,8 +191,8 @@ def make_bom(**args):
|
||||
'qty': 1,
|
||||
'uom': item_doc.stock_uom,
|
||||
'stock_uom': item_doc.stock_uom,
|
||||
'rate': item_doc.valuation_rate or args.rate
|
||||
'rate': item_doc.valuation_rate or args.rate,
|
||||
})
|
||||
|
||||
|
||||
bom.insert(ignore_permissions=True)
|
||||
bom.submit()
|
@ -292,6 +292,7 @@ class TestWorkOrder(unittest.TestCase):
|
||||
make_bom(item=fg_item, rate=1000, raw_materials = ['_Test FG Item', '_Test FG Non Stock Item'])
|
||||
|
||||
wo = make_wo_order_test_record(production_item = fg_item)
|
||||
|
||||
se = frappe.get_doc(make_stock_entry(wo.name, "Material Transfer for Manufacture", 1))
|
||||
se.insert()
|
||||
se.submit()
|
||||
|
@ -1 +0,0 @@
|
||||
from __future__ import unicode_literals
|
@ -1,155 +0,0 @@
|
||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
frappe.pages['production-analytics'].on_page_load = function(wrapper) {
|
||||
frappe.ui.make_app_page({
|
||||
parent: wrapper,
|
||||
title: __('Production Analytics'),
|
||||
single_column: true
|
||||
});
|
||||
|
||||
new erpnext.ProductionAnalytics(wrapper);
|
||||
|
||||
frappe.breadcrumbs.add("Manufacturing");
|
||||
}
|
||||
|
||||
erpnext.ProductionAnalytics = frappe.views.GridReportWithPlot.extend({
|
||||
init: function(wrapper) {
|
||||
this._super({
|
||||
title: __("Production Analytics"),
|
||||
parent: $(wrapper).find('.layout-main'),
|
||||
page: wrapper.page,
|
||||
doctypes: ["Item", "Company", "Fiscal Year", "Work Order"]
|
||||
});
|
||||
|
||||
},
|
||||
setup_columns: function() {
|
||||
|
||||
var std_columns = [
|
||||
{id: "name", name: __("Status"), field: "name", width: 100}
|
||||
];
|
||||
|
||||
this.make_date_range_columns();
|
||||
this.columns = std_columns.concat(this.columns);
|
||||
},
|
||||
filters: [
|
||||
{fieldtype:"Select", label: __("Company"), link:"Company", fieldname: "company",
|
||||
default_value: __("Select Company...")},
|
||||
{fieldtype:"Date", label: __("From Date"), fieldname: "from_date"},
|
||||
{fieldtype:"Date", label: __("To Date"), fieldname: "to_date"},
|
||||
{fieldtype:"Select", label: __("Range"), fieldname: "range",
|
||||
options:[{label: __("Daily"), value: "Daily"}, {label: __("Weekly"), value: "Weekly"},
|
||||
{label: __("Monthly"), value: "Monthly"}, {label: __("Quarterly"), value: "Quarterly"},
|
||||
{label: __("Yearly"), value: "Yearly"}]}
|
||||
],
|
||||
setup_filters: function() {
|
||||
var me = this;
|
||||
this._super();
|
||||
|
||||
this.trigger_refresh_on_change(["company"]);
|
||||
this.trigger_refresh_on_change(["range"]);
|
||||
|
||||
this.show_zero_check();
|
||||
|
||||
},
|
||||
init_filter_values: function() {
|
||||
this._super();
|
||||
this.filter_inputs.range.val('Monthly');
|
||||
},
|
||||
setup_chart: function() {
|
||||
var me = this;
|
||||
|
||||
var chart_data = this.get_chart_data ? this.get_chart_data() : null;
|
||||
|
||||
const parent = this.wrapper.find('.chart')[0];
|
||||
this.chart = new Chart(parent, {
|
||||
height: 200,
|
||||
data: chart_data,
|
||||
type: 'line'
|
||||
});
|
||||
},
|
||||
set_default_values: function() {
|
||||
var values = {
|
||||
from_date: frappe.datetime.str_to_user(frappe.datetime.add_months(frappe.datetime.now_datetime(),-12) ),
|
||||
to_date: frappe.datetime.str_to_user(frappe.datetime.add_months(frappe.datetime.now_datetime(),1))
|
||||
}
|
||||
|
||||
var me = this;
|
||||
$.each(values, function(i, v) {
|
||||
if(me.filter_inputs[i] && !me.filter_inputs[i].val())
|
||||
me.filter_inputs[i].val(v);
|
||||
})
|
||||
},
|
||||
|
||||
prepare_data: function() {
|
||||
// add Opening, Closing, Totals rows
|
||||
// if filtered by account and / or voucher
|
||||
var me = this;
|
||||
var all_open_orders = {name:"All Work Orders", "id": "all-open-pos",
|
||||
checked:true};
|
||||
var not_started = {name:"Not Started", "id":"not-started-pos",
|
||||
checked:true};
|
||||
var overdue = {name:"Overdue (Not Started)", "id":"overdue-pos",
|
||||
checked:true};
|
||||
var pending = {name:"Pending", "id":"pending-pos",
|
||||
checked:true};
|
||||
var completed = {name:"Completed", "id":"completed-pos",
|
||||
checked:true};
|
||||
|
||||
$.each(frappe.report_dump.data["Work Order"], function(i, d) {
|
||||
var dateobj = frappe.datetime.str_to_obj(d.creation);
|
||||
var date = frappe.datetime.str_to_user(d.creation.split(" ")[0]);
|
||||
|
||||
$.each(me.columns, function(i,col) {
|
||||
if (i > 1){
|
||||
var start_period = frappe.datetime.user_to_obj(frappe.datetime.str_to_user(col.id));
|
||||
var end_period = frappe.datetime.user_to_obj(frappe.datetime.str_to_user(col.name));
|
||||
var astart_date = frappe.datetime.user_to_obj(frappe.datetime.str_to_user(d.actual_start_date));
|
||||
var planned_start_date = frappe.datetime.user_to_obj(frappe.datetime.str_to_user(d.planned_start_date));
|
||||
var aend_date = frappe.datetime.user_to_obj(frappe.datetime.str_to_user(d.actual_end_date));
|
||||
var modified = frappe.datetime.user_to_obj(frappe.datetime.str_to_user(d.modified));
|
||||
|
||||
if (dateobj <= start_period || dateobj <= end_period) {
|
||||
all_open_orders[col.field] = flt(all_open_orders[col.field]) + 1;
|
||||
|
||||
if(d.status=="Completed") {
|
||||
if(aend_date < start_period || modified < start_period) {
|
||||
completed[col.field] = flt(completed[col.field]) + 1;
|
||||
}
|
||||
else if (astart_date < start_period) {
|
||||
pending[col.field] = flt(pending[col.field]) + 1;
|
||||
}
|
||||
else if (planned_start_date < start_period) {
|
||||
overdue[col.field] = flt(overdue[col.field]) + 1;
|
||||
} else {
|
||||
not_started[col.field] = flt(not_started[col.field]) + 1;
|
||||
}
|
||||
}else if(d.status == "In Process")
|
||||
{
|
||||
if (astart_date < start_period || modified < start_period){
|
||||
pending[col.field] = flt(pending[col.field]) + 1;
|
||||
}else if (planned_start_date < start_period) {
|
||||
overdue[col.field] = flt(overdue[col.field]) + 1;
|
||||
}else{
|
||||
not_started[col.field] = flt(not_started[col.field]) + 1;
|
||||
}
|
||||
}else if(d.status == "Not Started") {
|
||||
if (planned_start_date < start_period){
|
||||
overdue[col.field] = flt(overdue[col.field]) + 1;
|
||||
}else{
|
||||
not_started[col.field] = flt(not_started[col.field]) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
if(me.columns.length < 30){
|
||||
this.chart_area.toggle(true);
|
||||
}else {
|
||||
this.chart_area.toggle(false);
|
||||
}
|
||||
this.data = [all_open_orders, not_started, overdue, pending, completed];
|
||||
|
||||
}
|
||||
});
|
@ -1,26 +0,0 @@
|
||||
{
|
||||
"content": null,
|
||||
"creation": "2012-09-21 20:15:16",
|
||||
"docstatus": 0,
|
||||
"doctype": "Page",
|
||||
"icon": "fa fa-bar-chart",
|
||||
"idx": 1,
|
||||
"modified": "2017-02-20 17:33:05.913097",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "production-analytics",
|
||||
"owner": "Administrator",
|
||||
"page_name": "production-analytics",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Analytics"
|
||||
},
|
||||
{
|
||||
"role": "Manufacturing Manager"
|
||||
}
|
||||
],
|
||||
"script": null,
|
||||
"standard": "Yes",
|
||||
"style": null,
|
||||
"title": "Production Analytics"
|
||||
}
|
@ -39,8 +39,5 @@ frappe.query_reports["Production Analytics"] = {
|
||||
default: "Monthly",
|
||||
reqd: 1
|
||||
}
|
||||
],
|
||||
"formatter": function(value, row, column, data) {
|
||||
return value;
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -568,10 +568,10 @@ erpnext.patches.v11_0.remove_land_unit_icon
|
||||
erpnext.patches.v11_0.add_default_dispatch_notification_template
|
||||
erpnext.patches.v11_0.add_market_segments
|
||||
erpnext.patches.v11_0.add_sales_stages
|
||||
execute:frappe.delete_doc("Page", "Sales Analytics")
|
||||
execute:frappe.delete_doc("Page", "Purchase Analytics")
|
||||
execute:frappe.delete_doc("Page", "Stock Analytics")
|
||||
execute:frappe.delete_doc("Page", "Production Analytics")
|
||||
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.drop_column_max_days_allowed
|
||||
erpnext.patches.v11_0.change_healthcare_desktop_icons
|
||||
|
@ -3,12 +3,18 @@ from frappe import _
|
||||
|
||||
def execute():
|
||||
""" assign lft and rgt appropriately """
|
||||
if "Healthcare" not in frappe.get_active_domains():
|
||||
return
|
||||
|
||||
frappe.reload_doc("healthcare", "doctype", "healthcare_service_unit")
|
||||
frappe.reload_doc("healthcare", "doctype", "healthcare_service_unit_type")
|
||||
company = frappe.get_value("Company", {"domain": "Healthcare"}, "name")
|
||||
|
||||
if not frappe.db.exists("Healthcare Service Unit", _('All Healthcare Service Units')):
|
||||
if company:
|
||||
frappe.get_doc({
|
||||
'doctype': 'Healthcare Service Unit',
|
||||
'healthcare_service_unit_name': _('All Healthcare Service Units'),
|
||||
'is_group': 1
|
||||
'is_group': 1,
|
||||
'company': company
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
|
@ -32,7 +32,7 @@ def execute():
|
||||
'user': user,
|
||||
'default': 1
|
||||
})
|
||||
_doc.pos_profile_name = user + ' - ' + _doc.company
|
||||
|
||||
_doc.flags.ignore_validate = True
|
||||
_doc.flags.ignore_mandatory = True
|
||||
_doc.save()
|
@ -11,14 +11,14 @@ def execute():
|
||||
for pos in frappe.get_all(doctype, filters={'disabled': 0}):
|
||||
doc = frappe.get_doc(doctype, pos.name)
|
||||
|
||||
if not doc.user or doc.pos_profile_name: continue
|
||||
if not doc.user: continue
|
||||
|
||||
try:
|
||||
doc.pos_profile_name = doc.user + ' - ' + doc.company
|
||||
pos_profile_name = doc.user + ' - ' + doc.company
|
||||
doc.flags.ignore_validate = True
|
||||
doc.flags.ignore_mandatory = True
|
||||
doc.save()
|
||||
|
||||
frappe.rename_doc(doctype, doc.name, doc.pos_profile_name, force=True)
|
||||
frappe.rename_doc(doctype, doc.name, pos_profile_name, force=True)
|
||||
except frappe.LinkValidationError:
|
||||
frappe.db.set_value("POS Profile", doc.name, 'disabled', 1)
|
||||
|
@ -120,28 +120,3 @@ frappe.ui.form.on("Project Task", {
|
||||
frm.trigger('tasks_refresh');
|
||||
},
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Project", "validate", function (frm) {
|
||||
if (frm.doc.collect_progress == 1) {
|
||||
frappe.call({
|
||||
method: "erpnext.projects.doctype.project.project.times_check",
|
||||
args: {
|
||||
"from1": frm.doc.from,
|
||||
"to": frm.doc.to,
|
||||
"first_email": frm.doc.first_email,
|
||||
"second_email": frm.doc.second_email,
|
||||
"daily_time_to_send": frm.doc.daily_time_to_send,
|
||||
"weekly_time_to_send": frm.doc.weekly_time_to_send
|
||||
|
||||
},
|
||||
callback: function (r) {
|
||||
frm.set_value("from", r.message.from1);
|
||||
frm.set_value("to", r.message.to);
|
||||
frm.set_value("first_email", r.message.first_email);
|
||||
frm.set_value("second_email", r.message.second_email);
|
||||
frm.set_value("daily_time_to_send", r.message.daily_time_to_send);
|
||||
frm.set_value("weekly_time_to_send", r.message.weekly_time_to_send);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
@ -427,23 +427,6 @@ def weekly():
|
||||
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)
|
||||
|
||||
@frappe.whitelist()
|
||||
def times_check(from1, to, first_email, second_email, daily_time_to_send, weekly_time_to_send):
|
||||
from1 = datetime.datetime.strptime(from1, "%H:%M:%S.%f")
|
||||
from1 = from1.strftime("%H:00:00")
|
||||
to = datetime.datetime.strptime(to, "%H:%M:%S.%f")
|
||||
to = to.strftime("%H:00:00")
|
||||
first_email = datetime.datetime.strptime(first_email, "%H:%M:%S.%f")
|
||||
first_email = first_email.strftime("%H:00:00")
|
||||
second_email = datetime.datetime.strptime(second_email, "%H:%M:%S.%f")
|
||||
second_email = second_email.strftime("%H:00:00")
|
||||
daily_time_to_send = datetime.datetime.strptime(daily_time_to_send, "%H:%M:%S.%f")
|
||||
daily_time_to_send = daily_time_to_send.strftime("%H:00:00")
|
||||
weekly_time_to_send = datetime.datetime.strptime(weekly_time_to_send, "%H:%M:%S.%f")
|
||||
weekly_time_to_send = weekly_time_to_send.strftime("%H:00:00")
|
||||
return {"from1": from1, "to": to, "first_email": first_email, "second_email": second_email,"daily_time_to_send": daily_time_to_send, "weekly_time_to_send": weekly_time_to_send}
|
||||
|
||||
|
||||
#Call this function in order to generate the Project Update for a specific project
|
||||
def create_project_update(project):
|
||||
data = []
|
||||
|
@ -80,15 +80,15 @@ frappe.ui.form.on("Task", {
|
||||
}
|
||||
},
|
||||
|
||||
is_group: function(frm) {
|
||||
is_group: function (frm) {
|
||||
frappe.call({
|
||||
method:"erpnext.projects.doctype.task.task.check_if_child_exists",
|
||||
method: "erpnext.projects.doctype.task.task.check_if_child_exists",
|
||||
args: {
|
||||
name: frm.doc.name
|
||||
},
|
||||
callback: function(r){
|
||||
if(r.message){
|
||||
frappe.msgprint(__('Cannot convert it to non-group. Child Tasks exist.'));
|
||||
callback: function (r) {
|
||||
if (r.message.length > 0) {
|
||||
frappe.msgprint(__(`Cannot convert it to non-group. The following child Tasks exist: ${r.message.join(", ")}.`));
|
||||
frm.reload_doc();
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,15 @@
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe, json
|
||||
|
||||
from frappe.utils import getdate, date_diff, add_days, cstr
|
||||
import json
|
||||
|
||||
import frappe
|
||||
from frappe import _, throw
|
||||
from frappe.utils import add_days, cstr, date_diff, get_link_to_form, getdate
|
||||
from frappe.utils.nestedset import NestedSet
|
||||
|
||||
|
||||
class CircularReferenceError(frappe.ValidationError): pass
|
||||
|
||||
class Task(NestedSet):
|
||||
@ -157,8 +160,10 @@ class Task(NestedSet):
|
||||
|
||||
@frappe.whitelist()
|
||||
def check_if_child_exists(name):
|
||||
return frappe.db.sql("""select name from `tabTask`
|
||||
where parent_task = %s""", name)
|
||||
child_tasks = frappe.get_all("Task", filters={"parent_task": name})
|
||||
child_tasks = [get_link_to_form("Task", task.name) for task in child_tasks]
|
||||
return child_tasks
|
||||
|
||||
|
||||
def get_project(doctype, txt, searchfield, start, page_len, filters):
|
||||
from erpnext.controllers.queries import get_match_cond
|
||||
@ -237,4 +242,4 @@ def add_multiple_tasks(data, parent):
|
||||
new_task.insert()
|
||||
|
||||
def on_doctype_update():
|
||||
frappe.db.add_index("Task", ["lft", "rgt"])
|
||||
frappe.db.add_index("Task", ["lft", "rgt"])
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 0,
|
||||
@ -444,39 +445,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": "start_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 1,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "User",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "User",
|
||||
"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,
|
||||
@ -1032,7 +1000,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-08-28 14:44:32.912004",
|
||||
"modified": "2018-11-15 07:58:42.629845",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Projects",
|
||||
"name": "Timesheet",
|
||||
|
@ -346,6 +346,11 @@ body[data-route="pos"] .btn-more {
|
||||
body[data-route="pos"] .collapse-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
body[data-route="pos"] .page-actions {
|
||||
max-width: 110px;
|
||||
}
|
||||
}
|
||||
.price-info {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
|
@ -26,8 +26,8 @@ $(document).bind('toolbar_setup', function() {
|
||||
|
||||
$('<li><a data-link-type="forum" href="https://discuss.erpnext.com" \
|
||||
target="_blank">'+__('User Forum')+'</a></li>').insertBefore($help_menu);
|
||||
$('<li><a href="https://gitter.im/frappe/erpnext" \
|
||||
target="_blank">'+__('Chat')+'</a></li>').insertBefore($help_menu);
|
||||
$('<li class="gitter-chat-link"><a href="https://gitter.im/frappe/erpnext" \
|
||||
target="_blank">'+__('Gitter Chat')+'</a></li>').insertBefore($help_menu);
|
||||
$('<li><a href="https://github.com/frappe/erpnext/issues" \
|
||||
target="_blank">'+__('Report an Issue')+'</a></li>').insertBefore($help_menu);
|
||||
|
||||
|
@ -33,7 +33,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
item.margin_rate_or_amount = 0;
|
||||
item.rate_with_margin = 0;
|
||||
}
|
||||
item.base_rate_with_margin = item.rate_with_margin * flt(this.frm.doc.conversion_rate);
|
||||
item.base_rate_with_margin = item.rate_with_margin * flt(frm.doc.conversion_rate);
|
||||
|
||||
cur_frm.cscript.set_gross_profit(item);
|
||||
cur_frm.cscript.calculate_taxes_and_totals();
|
||||
@ -185,6 +185,12 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
}
|
||||
},
|
||||
|
||||
is_return: function() {
|
||||
if(!this.frm.doc.is_return && this.frm.doc.return_against) {
|
||||
this.frm.set_value('return_against', '');
|
||||
}
|
||||
},
|
||||
|
||||
setup_quality_inspection: function() {
|
||||
if(!in_list(["Delivery Note", "Sales Invoice", "Purchase Receipt", "Purchase Invoice"], this.frm.doc.doctype)) {
|
||||
return;
|
||||
|
@ -44,8 +44,8 @@ erpnext.timesheet.timer = function(frm, row, timestamp=0) {
|
||||
};
|
||||
|
||||
erpnext.timesheet.control_timer = function(frm, dialog, row, timestamp=0) {
|
||||
var $btn_start = $(".playpause .btn-start");
|
||||
var $btn_complete = $(".playpause .btn-complete");
|
||||
var $btn_start = dialog.$wrapper.find(".playpause .btn-start");
|
||||
var $btn_complete = dialog.$wrapper.find(".playpause .btn-complete");
|
||||
var interval = null;
|
||||
var currentIncrement = timestamp;
|
||||
var initialised = row ? true : false;
|
||||
|
32
erpnext/public/js/utils.js
Normal file → Executable file
32
erpnext/public/js/utils.js
Normal file → Executable file
@ -103,12 +103,32 @@ $.extend(erpnext, {
|
||||
$.extend(erpnext.utils, {
|
||||
set_party_dashboard_indicators: function(frm) {
|
||||
if(frm.doc.__onload && frm.doc.__onload.dashboard_info) {
|
||||
var info = frm.doc.__onload.dashboard_info;
|
||||
frm.dashboard.add_indicator(__('Annual Billing: {0}',
|
||||
[format_currency(info.billing_this_year, info.currency)]), 'blue');
|
||||
frm.dashboard.add_indicator(__('Total Unpaid: {0}',
|
||||
[format_currency(info.total_unpaid, info.currency)]),
|
||||
info.total_unpaid ? 'orange' : 'green');
|
||||
var company_wise_info = frm.doc.__onload.dashboard_info;
|
||||
if(company_wise_info.length > 1) {
|
||||
frm.dashboard.stats_area.removeClass('hidden');
|
||||
frm.dashboard.stats_area_row.addClass('flex');
|
||||
frm.dashboard.stats_area_row.css('flex-wrap', 'wrap');
|
||||
company_wise_info.forEach(function(info) {
|
||||
frm.dashboard.stats_area_row.append(
|
||||
'<div class="flex-column col-xs-6">'+
|
||||
'<div style="margin-bottom:20px"><h6>'+info.company+'</h6></div>'+
|
||||
'<div class="badge-link small" style="margin-bottom:10px">Annual Billing: '
|
||||
+format_currency(info.billing_this_year, info.currency)+'</div>'+
|
||||
'<div class="badge-link small" style="margin-bottom:20px">Total Unpaid: '
|
||||
+format_currency(info.total_unpaid, info.currency)+'</div>'+
|
||||
'</div>'
|
||||
);
|
||||
});
|
||||
}
|
||||
else if (company_wise_info.length === 1) {
|
||||
frm.dashboard.stats_area.removeClass('hidden');
|
||||
frm.dashboard.stats_area_row.append(
|
||||
'</div><div class="col-xs-6 small" style="margin-bottom:10px">Annual Billing: <b>'
|
||||
+format_currency(company_wise_info[0].billing_this_year, company_wise_info[0].currency)+'</b></div>' +
|
||||
'<div class="col-xs-6 small" style="margin-bottom:10px">Total Unpaid: <b>'
|
||||
+format_currency(company_wise_info[0].total_unpaid, company_wise_info[0].currency)+'</b></div>'
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -320,7 +320,7 @@ frappe.ui.form.ItemQuickEntryForm = frappe.ui.form.QuickEntryForm.extend({
|
||||
["attribute_value", "like", e.target.value + "%"]
|
||||
],
|
||||
fields: ["attribute_value"],
|
||||
parent: "Item"
|
||||
parent: "Item Attribute"
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
|
@ -11,14 +11,18 @@ from erpnext.accounts.utils import get_fiscal_year
|
||||
from frappe.utils import today
|
||||
|
||||
def setup(company=None, patch=True):
|
||||
setup_company_independent_fixtures()
|
||||
if not patch:
|
||||
update_address_template()
|
||||
make_fixtures(company)
|
||||
|
||||
# TODO: for all countries
|
||||
def setup_company_independent_fixtures():
|
||||
make_custom_fields()
|
||||
add_permissions()
|
||||
add_custom_roles_for_reports()
|
||||
frappe.enqueue('erpnext.regional.india.setup.add_hsn_sac_codes', now=frappe.flags.in_test)
|
||||
add_print_formats()
|
||||
if not patch:
|
||||
update_address_template()
|
||||
make_fixtures(company)
|
||||
|
||||
def update_address_template():
|
||||
with open(os.path.join(os.path.dirname(__file__), 'address_template.html'), 'r') as f:
|
||||
@ -350,12 +354,17 @@ def set_tax_withholding_category(company):
|
||||
|
||||
def set_tds_account(docs, company):
|
||||
abbr = frappe.get_value("Company", company, "abbr")
|
||||
docs.extend([
|
||||
{
|
||||
"doctype": "Account", "account_name": "TDS Payable", "account_type": "Tax",
|
||||
"parent_account": "Duties and Taxes - {0}".format(abbr), "company": company
|
||||
}
|
||||
])
|
||||
parent_account = frappe.db.get_value("Account", filters = {"account_name": "Duties and Taxes", "company": company})
|
||||
if parent_account:
|
||||
docs.extend([
|
||||
{
|
||||
"doctype": "Account",
|
||||
"account_name": "TDS Payable",
|
||||
"account_type": "Tax",
|
||||
"parent_account": parent_account,
|
||||
"company": company
|
||||
}
|
||||
])
|
||||
|
||||
def get_tds_details(accounts, fiscal_year):
|
||||
# bootstrap default tax withholding sections
|
||||
|
@ -206,7 +206,7 @@ cur_frm.cscript['Declare Order Lost'] = function(){
|
||||
|
||||
}
|
||||
|
||||
frappe.ui.form.on("Quotation Item", "items_on_form_rendered", function(frm, cdt, cdn) {
|
||||
frappe.ui.form.on("Quotation Item", "items_on_form_rendered", "packed_items_on_form_rendered", function(frm, cdt, cdn) {
|
||||
// enable tax_amount field if Actual
|
||||
})
|
||||
|
||||
|
@ -32,7 +32,6 @@ class SalesOrder(SellingController):
|
||||
|
||||
def validate(self):
|
||||
super(SalesOrder, self).validate()
|
||||
|
||||
self.validate_order_type()
|
||||
self.validate_delivery_date()
|
||||
self.validate_proj_cust()
|
||||
@ -342,9 +341,11 @@ class SalesOrder(SellingController):
|
||||
|
||||
delivered_qty += item.delivered_qty
|
||||
tot_qty += item.qty
|
||||
|
||||
self.db_set("per_delivered", flt(delivered_qty/tot_qty) * 100,
|
||||
update_modified=False)
|
||||
|
||||
if tot_qty != 0:
|
||||
self.db_set("per_delivered", flt(delivered_qty/tot_qty) * 100,
|
||||
update_modified=False)
|
||||
|
||||
|
||||
def set_indicator(self):
|
||||
"""Set indicator for portal"""
|
||||
|
@ -125,6 +125,8 @@ def search_serial_or_batch_or_barcode_number(search_value):
|
||||
if batch_no_data:
|
||||
return batch_no_data
|
||||
|
||||
return {}
|
||||
|
||||
def get_conditions(item_code, serial_no, batch_no, barcode):
|
||||
if serial_no or batch_no or barcode:
|
||||
return frappe.db.escape(item_code), "i.name = %(item_code)s"
|
||||
|
@ -1 +0,0 @@
|
||||
Trends of sales by Item, Item Group, Customer etc.
|
@ -1 +0,0 @@
|
||||
from __future__ import unicode_literals
|
@ -1,246 +0,0 @@
|
||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
frappe.pages['sales-analytics'].on_page_load = function(wrapper) {
|
||||
frappe.ui.make_app_page({
|
||||
parent: wrapper,
|
||||
title: __('Sales Analytics'),
|
||||
single_column: true
|
||||
});
|
||||
new erpnext.SalesAnalytics(wrapper);
|
||||
|
||||
|
||||
frappe.breadcrumbs.add("Selling")
|
||||
|
||||
};
|
||||
|
||||
erpnext.SalesAnalytics = frappe.views.TreeGridReport.extend({
|
||||
init: function(wrapper) {
|
||||
this._super({
|
||||
title: __("Sales Analytics"),
|
||||
parent: $(wrapper).find('.layout-main'),
|
||||
page: wrapper.page,
|
||||
doctypes: ["Item", "Item Group", "Customer", "Customer Group", "Company", "Territory",
|
||||
"Fiscal Year", "Sales Invoice", "Sales Invoice Item",
|
||||
"Sales Order", "Sales Order Item[Sales Analytics]",
|
||||
"Delivery Note", "Delivery Note Item[Sales Analytics]"],
|
||||
tree_grid: { show: true }
|
||||
});
|
||||
|
||||
this.tree_grids = {
|
||||
"Customer Group": {
|
||||
label: __("Customer Group / Customer"),
|
||||
show: true,
|
||||
item_key: "customer",
|
||||
parent_field: "parent_customer_group",
|
||||
formatter: function(item) {
|
||||
return item.customer_name? item.customer_name + " (" + item.name + ")" : item.name;
|
||||
}
|
||||
},
|
||||
"Customer": {
|
||||
label: __("Customer"),
|
||||
show: false,
|
||||
item_key: "customer",
|
||||
formatter: function(item) {
|
||||
return item.customer_name? item.customer_name + " (" + item.name + ")" : item.name;
|
||||
}
|
||||
},
|
||||
"Item Group": {
|
||||
label: __("Item"),
|
||||
show: true,
|
||||
parent_field: "parent_item_group",
|
||||
item_key: "item_code",
|
||||
formatter: function(item) {
|
||||
return item.name;
|
||||
}
|
||||
},
|
||||
"Item": {
|
||||
label: __("Item"),
|
||||
show: false,
|
||||
item_key: "item_code",
|
||||
formatter: function(item) {
|
||||
return item.name;
|
||||
}
|
||||
},
|
||||
"Territory": {
|
||||
label: __("Territory / Customer"),
|
||||
show: true,
|
||||
item_key: "customer",
|
||||
parent_field: "parent_territory",
|
||||
formatter: function(item) {
|
||||
return item.customer_name? item.customer_name + " (" + item.name + ")" : item.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
setup_columns: function() {
|
||||
this.tree_grid = this.tree_grids[this.tree_type];
|
||||
|
||||
var std_columns = [
|
||||
{id: "name", name: this.tree_grid.label, field: "name", width: 300},
|
||||
{id: "total", name: "Total", field: "total", plot: false,
|
||||
formatter: this.currency_formatter}
|
||||
];
|
||||
|
||||
this.make_date_range_columns();
|
||||
this.columns = std_columns.concat(this.columns);
|
||||
},
|
||||
filters: [
|
||||
{fieldtype:"Select", fieldname: "tree_type", label: __("Tree Type"), options:["Customer Group", "Customer",
|
||||
"Item Group", "Item", "Territory"],
|
||||
filter: function(val, item, opts, me) {
|
||||
return me.apply_zero_filter(val, item, opts, me);
|
||||
}},
|
||||
{fieldtype:"Select", fieldname: "based_on", label: __("Based On"), options:["Sales Invoice",
|
||||
"Sales Order", "Delivery Note"]},
|
||||
{fieldtype:"Select", fieldname: "value_or_qty", label: __("Value or Qty"),
|
||||
options:[{label: __("Value"), value: "Value"}, {label: __("Quantity"), value: "Quantity"}]},
|
||||
{fieldtype:"Date", fieldname: "from_date", label: __("From Date")},
|
||||
{fieldtype:"Label", fieldname: "to", label: __("To")},
|
||||
{fieldtype:"Date", fieldname: "to_date", label: __("To Date")},
|
||||
{fieldtype:"Select", fieldname: "company", label: __("Company"), link:"Company",
|
||||
default_value: __("Select Company...")},
|
||||
{fieldtype:"Select", label: __("Range"), fieldname: "range",
|
||||
options:[{label: __("Daily"), value: "Daily"}, {label: __("Weekly"), value: "Weekly"},
|
||||
{label: __("Monthly"), value: "Monthly"}, {label: __("Quarterly"), value: "Quarterly"},
|
||||
{label: __("Yearly"), value: "Yearly"}]}
|
||||
],
|
||||
setup_filters: function() {
|
||||
var me = this;
|
||||
this._super();
|
||||
|
||||
this.trigger_refresh_on_change(["value_or_qty", "tree_type", "based_on", "company"]);
|
||||
|
||||
this.show_zero_check();
|
||||
},
|
||||
init_filter_values: function() {
|
||||
this._super();
|
||||
this.filter_inputs.range.val('Monthly');
|
||||
},
|
||||
prepare_data: function() {
|
||||
var me = this;
|
||||
if (!this.tl) {
|
||||
// add 'Not Set' Customer & Item
|
||||
// (Customer / Item are not mandatory!!)
|
||||
frappe.report_dump.data["Customer"].push({
|
||||
name: "Not Set",
|
||||
parent_customer_group: "All Customer Groups",
|
||||
parent_territory: "All Territories",
|
||||
id: "Not Set",
|
||||
});
|
||||
|
||||
frappe.report_dump.data["Item"].push({
|
||||
name: "Not Set",
|
||||
parent_item_group: "All Item Groups",
|
||||
id: "Not Set",
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.tl || !this.tl[this.based_on]) {
|
||||
this.make_transaction_list(this.based_on, this.based_on + " Item");
|
||||
}
|
||||
|
||||
if(!this.data || me.item_type != me.tree_type) {
|
||||
if(me.tree_type=='Customer') {
|
||||
var items = frappe.report_dump.data["Customer"];
|
||||
} if(me.tree_type=='Customer Group') {
|
||||
var items = this.prepare_tree("Customer", "Customer Group");
|
||||
} else if(me.tree_type=="Item Group") {
|
||||
var items = this.prepare_tree("Item", "Item Group");
|
||||
} else if(me.tree_type=="Item") {
|
||||
var items = frappe.report_dump.data["Item"];
|
||||
} else if(me.tree_type=="Territory") {
|
||||
var items = this.prepare_tree("Customer", "Territory");
|
||||
}
|
||||
|
||||
me.item_type = me.tree_type
|
||||
me.parent_map = {};
|
||||
me.item_by_name = {};
|
||||
me.data = [];
|
||||
|
||||
$.each(items, function(i, v) {
|
||||
var d = copy_dict(v);
|
||||
|
||||
me.data.push(d);
|
||||
me.item_by_name[d.name] = d;
|
||||
if(d[me.tree_grid.parent_field]) {
|
||||
me.parent_map[d.name] = d[me.tree_grid.parent_field];
|
||||
}
|
||||
me.reset_item_values(d);
|
||||
});
|
||||
|
||||
this.set_indent();
|
||||
|
||||
} else {
|
||||
// otherwise, only reset values
|
||||
$.each(this.data, function(i, d) {
|
||||
me.reset_item_values(d);
|
||||
});
|
||||
}
|
||||
|
||||
this.prepare_balances();
|
||||
if(me.tree_grid.show) {
|
||||
this.set_totals(false);
|
||||
this.update_groups();
|
||||
} else {
|
||||
this.set_totals(true);
|
||||
}
|
||||
|
||||
},
|
||||
prepare_balances: function() {
|
||||
var me = this;
|
||||
var from_date = frappe.datetime.str_to_obj(this.from_date);
|
||||
var to_date = frappe.datetime.str_to_obj(this.to_date);
|
||||
var is_val = this.value_or_qty == 'Value';
|
||||
|
||||
$.each(this.tl[this.based_on], function(i, tl) {
|
||||
if (me.is_default('company') ? true : tl.company === me.company) {
|
||||
var posting_date = frappe.datetime.str_to_obj(tl.posting_date);
|
||||
if (posting_date >= from_date && posting_date <= to_date) {
|
||||
var item = me.item_by_name[tl[me.tree_grid.item_key]] ||
|
||||
me.item_by_name['Not Set'];
|
||||
if(item){
|
||||
item[me.column_map[tl.posting_date].field] += (is_val ? tl.base_net_amount : tl.qty);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
update_groups: function() {
|
||||
var me = this;
|
||||
|
||||
$.each(this.data, function(i, item) {
|
||||
var parent = me.parent_map[item.name];
|
||||
while(parent) {
|
||||
var parent_group = me.item_by_name[parent];
|
||||
|
||||
$.each(me.columns, function(c, col) {
|
||||
if (col.formatter == me.currency_formatter) {
|
||||
parent_group[col.field] =
|
||||
flt(parent_group[col.field])
|
||||
+ flt(item[col.field]);
|
||||
}
|
||||
});
|
||||
parent = me.parent_map[parent];
|
||||
}
|
||||
});
|
||||
},
|
||||
set_totals: function(sort) {
|
||||
var me = this;
|
||||
var checked = false;
|
||||
$.each(this.data, function(i, d) {
|
||||
d.total = 0.0;
|
||||
$.each(me.columns, function(i, col) {
|
||||
if(col.formatter==me.currency_formatter && !col.hidden && col.field!="total")
|
||||
d.total += d[col.field];
|
||||
if(d.checked) checked = true;
|
||||
})
|
||||
});
|
||||
|
||||
if(sort)this.data = this.data.sort(function(a, b) { return a.total < b.total; });
|
||||
|
||||
if(!this.checked) {
|
||||
this.data[0].checked = true;
|
||||
}
|
||||
}
|
||||
});
|
@ -1,26 +0,0 @@
|
||||
{
|
||||
"creation": "2012-09-21 20:15:12.000000",
|
||||
"docstatus": 0,
|
||||
"doctype": "Page",
|
||||
"icon": "fa fa-bar-chart",
|
||||
"idx": 1,
|
||||
"modified": "2013-07-11 14:43:59.000000",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "sales-analytics",
|
||||
"owner": "Administrator",
|
||||
"page_name": "sales-analytics",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Analytics"
|
||||
},
|
||||
{
|
||||
"role": "Sales Manager"
|
||||
},
|
||||
{
|
||||
"role": "Maintenance Manager"
|
||||
}
|
||||
],
|
||||
"standard": "Yes",
|
||||
"title": "Sales Analytics"
|
||||
}
|
@ -6,8 +6,7 @@
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"letter_head": "Test",
|
||||
"is_standard": "Yes",
|
||||
"modified": "2018-06-01 09:39:39.604944",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
@ -30,4 +29,4 @@
|
||||
"role": "Accounts User"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -67,11 +67,8 @@ frappe.query_reports["Sales Analytics"] = {
|
||||
reqd: 1
|
||||
}
|
||||
],
|
||||
"formatter": function(value, row, column, data) {
|
||||
if(!value){
|
||||
value = 0
|
||||
}
|
||||
return value;
|
||||
after_datatable_render: function(datatable_obj) {
|
||||
$(datatable_obj.wrapper).find(".dt-row-0").find('input[type=checkbox]').click();
|
||||
},
|
||||
get_datatable_options(options) {
|
||||
return Object.assign(options, {
|
||||
@ -79,11 +76,11 @@ frappe.query_reports["Sales Analytics"] = {
|
||||
events: {
|
||||
onCheckRow: function(data) {
|
||||
row_name = data[2].content;
|
||||
row_values = data.slice(5).map(function (column) {
|
||||
length = data.length
|
||||
row_values = data.slice(4,length-1).map(function (column) {
|
||||
return column.content;
|
||||
})
|
||||
|
||||
entry = {
|
||||
entry = {
|
||||
'name':row_name,
|
||||
'values':row_values
|
||||
}
|
||||
@ -109,15 +106,15 @@ frappe.query_reports["Sales Analytics"] = {
|
||||
labels: raw_data.labels,
|
||||
datasets: new_datasets
|
||||
}
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
frappe.query_report.chart.update(new_data)
|
||||
},200)
|
||||
|
||||
|
||||
}, 500)
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
frappe.query_report.chart.draw(true);
|
||||
}, 800)
|
||||
}, 1000)
|
||||
|
||||
frappe.query_report.raw_chart_data = new_data;
|
||||
},
|
||||
|
@ -166,7 +166,7 @@ class Analytics(object):
|
||||
for entity, period_data in iteritems(self.entity_periodic_data):
|
||||
row = {
|
||||
"entity": entity,
|
||||
"entity_name": self.entity_names.get(entity)
|
||||
"entity_name": self.entity_names.get(entity)
|
||||
}
|
||||
total = 0
|
||||
for dummy, end_date in self.periodic_daterange:
|
||||
@ -177,7 +177,7 @@ class Analytics(object):
|
||||
|
||||
row["total"] = total
|
||||
self.data.append(row)
|
||||
|
||||
|
||||
def get_rows_by_group(self):
|
||||
self.get_periodic_data()
|
||||
out = []
|
||||
@ -185,7 +185,7 @@ class Analytics(object):
|
||||
for d in reversed(self.group_entries):
|
||||
row = {
|
||||
"entity": d.name,
|
||||
"indent": self.depth_map.get(d.name)
|
||||
"indent": self.depth_map.get(d.name)
|
||||
}
|
||||
total = 0
|
||||
for dummy, end_date in self.periodic_daterange:
|
||||
@ -212,11 +212,11 @@ class Analytics(object):
|
||||
|
||||
def get_period(self, posting_date):
|
||||
if self.filters.range == 'Weekly':
|
||||
period = "Week " + str(posting_date.isocalendar()[1])
|
||||
period = "Week " + str(posting_date.isocalendar()[1]) + " " + str(posting_date.year)
|
||||
elif self.filters.range == 'Monthly':
|
||||
period = self.months[posting_date.month - 1]
|
||||
period = str(self.months[posting_date.month - 1]) + " " + str(posting_date.year)
|
||||
elif self.filters.range == 'Quarterly':
|
||||
period = "Quarter " + str(((posting_date.month-1)//3)+1)
|
||||
period = "Quarter " + str(((posting_date.month-1)//3)+1) +" " + str(posting_date.year)
|
||||
else:
|
||||
year = get_fiscal_year(posting_date, company=self.filters.company)
|
||||
period = str(year[2])
|
||||
@ -275,12 +275,12 @@ class Analytics(object):
|
||||
self.parent_child_map = frappe._dict(frappe.db.sql(""" select name, supplier_group from `tabSupplier`"""))
|
||||
|
||||
def get_chart_data(self):
|
||||
labels = [d.get("label") for d in self.columns[3:]]
|
||||
length = len(self.columns)
|
||||
labels = [d.get("label") for d in self.columns[2:length-1]]
|
||||
self.chart = {
|
||||
"data": {
|
||||
'labels': labels,
|
||||
'datasets':[
|
||||
]
|
||||
'datasets':[]
|
||||
},
|
||||
"type": "line"
|
||||
}
|
@ -8,19 +8,23 @@ from erpnext.selling.report.sales_analytics.sales_analytics import execute
|
||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||
|
||||
class TestAnalytics(unittest.TestCase):
|
||||
def test_sales_analytics(self):
|
||||
frappe.db.sql("delete from `tabSales Order` where company='_Test Company 2'")
|
||||
|
||||
def tearDown(self):
|
||||
frappe.db.sql(""" DELETE FROM `tabSales Order` """)
|
||||
create_sales_orders()
|
||||
|
||||
def test_by_entity(self):
|
||||
create_sales_order()
|
||||
self.compare_result_for_customer()
|
||||
self.compare_result_for_customer_group()
|
||||
self.compare_result_for_customer_based_on_quantity()
|
||||
|
||||
|
||||
def compare_result_for_customer(self):
|
||||
filters = {
|
||||
'doc_type': 'Sales Order',
|
||||
'range': 'Monthly',
|
||||
'to_date': '2018-03-31',
|
||||
'tree_type': 'Customer',
|
||||
'company': '_Test Company',
|
||||
'company': '_Test Company 2',
|
||||
'from_date': '2017-04-01',
|
||||
'value_quantity': 'Value'
|
||||
}
|
||||
@ -31,153 +35,97 @@ class TestAnalytics(unittest.TestCase):
|
||||
{
|
||||
"entity": "_Test Customer 1",
|
||||
"entity_name": "_Test Customer 1",
|
||||
"apr": 0.0,
|
||||
"may": 0.0,
|
||||
"jun": 0.0,
|
||||
"jul": 0.0,
|
||||
"aug": 0.0,
|
||||
"sep": 0.0,
|
||||
"oct": 0.0,
|
||||
"nov": 0.0,
|
||||
"dec": 0.0,
|
||||
"jan": 0.0,
|
||||
"feb": 2000.0,
|
||||
"mar": 0.0,
|
||||
"apr_2017": 0.0,
|
||||
"may_2017": 0.0,
|
||||
"jun_2017": 0.0,
|
||||
"jul_2017": 0.0,
|
||||
"aug_2017": 0.0,
|
||||
"oct_2017": 0.0,
|
||||
"sep_2017": 0.0,
|
||||
"nov_2017": 0.0,
|
||||
"dec_2017": 0.0,
|
||||
"jan_2018": 0.0,
|
||||
"feb_2018": 2000.0,
|
||||
"mar_2018": 0.0,
|
||||
"total":2000.0
|
||||
},
|
||||
{
|
||||
"entity": "_Test Customer 3",
|
||||
"entity_name": "_Test Customer 3",
|
||||
"apr": 0.0,
|
||||
"may": 0.0,
|
||||
"jun": 2000.0,
|
||||
"jul": 1000.0,
|
||||
"aug": 0.0,
|
||||
"sep": 0.0,
|
||||
"oct": 0.0,
|
||||
"nov": 0.0,
|
||||
"dec": 0.0,
|
||||
"jan": 0.0,
|
||||
"feb": 0.0,
|
||||
"mar": 0.0,
|
||||
"total": 3000.0
|
||||
},
|
||||
{
|
||||
"entity": "_Test Customer 2",
|
||||
"entity_name": "_Test Customer 2",
|
||||
"apr": 0.0,
|
||||
"may": 0.0,
|
||||
"jun": 0.0,
|
||||
"jul": 0.0,
|
||||
"aug": 0.0,
|
||||
"sep": 1500.0,
|
||||
"oct": 1000.0,
|
||||
"nov": 0.0,
|
||||
"dec": 0.0,
|
||||
"jan": 0.0,
|
||||
"feb": 0.0,
|
||||
"mar": 0.0,
|
||||
"apr_2017": 0.0,
|
||||
"may_2017": 0.0,
|
||||
"jun_2017": 0.0,
|
||||
"jul_2017": 0.0,
|
||||
"aug_2017": 0.0,
|
||||
"sep_2017": 1500.0,
|
||||
"oct_2017": 1000.0,
|
||||
"nov_2017": 0.0,
|
||||
"dec_2017": 0.0,
|
||||
"jan_2018": 0.0,
|
||||
"feb_2018": 0.0,
|
||||
"mar_2018": 0.0,
|
||||
"total":2500.0
|
||||
},
|
||||
{
|
||||
"entity": "_Test Customer 3",
|
||||
"entity_name": "_Test Customer 3",
|
||||
"apr_2017": 0.0,
|
||||
"may_2017": 0.0,
|
||||
"jun_2017": 2000.0,
|
||||
"jul_2017": 1000.0,
|
||||
"aug_2017": 0.0,
|
||||
"sep_2017": 0.0,
|
||||
"oct_2017": 0.0,
|
||||
"nov_2017": 0.0,
|
||||
"dec_2017": 0.0,
|
||||
"jan_2018": 0.0,
|
||||
"feb_2018": 0.0,
|
||||
"mar_2018": 0.0,
|
||||
"total": 3000.0
|
||||
}
|
||||
]
|
||||
self.assertEqual(expected_data, report[1])
|
||||
|
||||
def test_by_group(self):
|
||||
create_sales_order()
|
||||
result = sorted(report[1], key=lambda k: k['entity'])
|
||||
self.assertEqual(expected_data, result)
|
||||
|
||||
def compare_result_for_customer_group(self):
|
||||
filters = {
|
||||
'doc_type': 'Sales Order',
|
||||
'range': 'Monthly',
|
||||
'to_date': '2018-03-31',
|
||||
'tree_type': 'Customer Group',
|
||||
'company': '_Test Company',
|
||||
'company': '_Test Company 2',
|
||||
'from_date': '2017-04-01',
|
||||
'value_quantity': 'Value'
|
||||
}
|
||||
|
||||
report = execute(filters)
|
||||
|
||||
expected_data = [
|
||||
{
|
||||
"entity": "All Customer Groups",
|
||||
"indent": 0,
|
||||
"apr": 0.0,
|
||||
"may": 0.0,
|
||||
"jun": 2000.0,
|
||||
"jul": 1000.0,
|
||||
"aug": 0.0,
|
||||
"sep": 1500.0,
|
||||
"oct": 1000.0,
|
||||
"nov": 0.0,
|
||||
"dec": 0.0,
|
||||
"jan": 0.0,
|
||||
"feb": 2000.0,
|
||||
"mar": 0.0,
|
||||
"total":7500.0
|
||||
},
|
||||
{
|
||||
"entity": "Individual",
|
||||
"indent": 1,
|
||||
"apr": 0.0,
|
||||
"may": 0.0,
|
||||
"jun": 0.0,
|
||||
"jul": 0.0,
|
||||
"aug": 0.0,
|
||||
"sep": 0.0,
|
||||
"oct": 0.0,
|
||||
"nov": 0.0,
|
||||
"dec": 0.0,
|
||||
"jan": 0.0,
|
||||
"feb": 0.0,
|
||||
"mar": 0.0,
|
||||
"total": 0.0
|
||||
},
|
||||
{
|
||||
"entity": "_Test Customer Group",
|
||||
"indent": 1,
|
||||
"apr": 0.0,
|
||||
"may": 0.0,
|
||||
"jun": 0.0,
|
||||
"jul": 0.0,
|
||||
"aug": 0.0,
|
||||
"sep": 0.0,
|
||||
"oct": 0.0,
|
||||
"nov": 0.0,
|
||||
"dec": 0.0,
|
||||
"jan": 0.0,
|
||||
"feb": 0.0,
|
||||
"mar": 0.0,
|
||||
"total":0.0
|
||||
},
|
||||
{
|
||||
"entity": "_Test Customer Group 1",
|
||||
"indent": 1,
|
||||
"apr": 0.0,
|
||||
"may": 0.0,
|
||||
"jun": 0.0,
|
||||
"jul": 0.0,
|
||||
"aug": 0.0,
|
||||
"sep": 0.0,
|
||||
"oct": 0.0,
|
||||
"nov": 0.0,
|
||||
"dec": 0.0,
|
||||
"jan": 0.0,
|
||||
"feb": 0.0,
|
||||
"mar": 0.0,
|
||||
"total":0.0
|
||||
}
|
||||
]
|
||||
self.assertEqual(expected_data, report[1])
|
||||
|
||||
def test_by_quantity(self):
|
||||
create_sales_order()
|
||||
expected_first_row = {
|
||||
"entity": "All Customer Groups",
|
||||
"indent": 0,
|
||||
"apr_2017": 0.0,
|
||||
"may_2017": 0.0,
|
||||
"jun_2017": 2000.0,
|
||||
"jul_2017": 1000.0,
|
||||
"aug_2017": 0.0,
|
||||
"sep_2017": 1500.0,
|
||||
"oct_2017": 1000.0,
|
||||
"nov_2017": 0.0,
|
||||
"dec_2017": 0.0,
|
||||
"jan_2018": 0.0,
|
||||
"feb_2018": 2000.0,
|
||||
"mar_2018": 0.0,
|
||||
"total":7500.0
|
||||
}
|
||||
self.assertEqual(expected_first_row, report[1][0])
|
||||
|
||||
def compare_result_for_customer_based_on_quantity(self):
|
||||
filters = {
|
||||
'doc_type': 'Sales Order',
|
||||
'range': 'Monthly',
|
||||
'to_date': '2018-03-31',
|
||||
'tree_type': 'Customer',
|
||||
'company': '_Test Company',
|
||||
'company': '_Test Company 2',
|
||||
'from_date': '2017-04-01',
|
||||
'value_quantity': 'Quantity'
|
||||
}
|
||||
@ -188,63 +136,93 @@ class TestAnalytics(unittest.TestCase):
|
||||
{
|
||||
"entity": "_Test Customer 1",
|
||||
"entity_name": "_Test Customer 1",
|
||||
"apr": 0.0,
|
||||
"may": 0.0,
|
||||
"jun": 0.0,
|
||||
"jul": 0.0,
|
||||
"aug": 0.0,
|
||||
"sep": 0.0,
|
||||
"oct": 0.0,
|
||||
"nov": 0.0,
|
||||
"dec": 0.0,
|
||||
"jan": 0.0,
|
||||
"feb": 20.0,
|
||||
"mar": 0.0,
|
||||
"apr_2017": 0.0,
|
||||
"may_2017": 0.0,
|
||||
"jun_2017": 0.0,
|
||||
"jul_2017": 0.0,
|
||||
"aug_2017": 0.0,
|
||||
"sep_2017": 0.0,
|
||||
"oct_2017": 0.0,
|
||||
"nov_2017": 0.0,
|
||||
"dec_2017": 0.0,
|
||||
"jan_2018": 0.0,
|
||||
"feb_2018": 20.0,
|
||||
"mar_2018": 0.0,
|
||||
"total":20.0
|
||||
},
|
||||
{
|
||||
"entity": "_Test Customer 3",
|
||||
"entity_name": "_Test Customer 3",
|
||||
"apr": 0.0,
|
||||
"may": 0.0,
|
||||
"jun": 20.0,
|
||||
"jul": 10.0,
|
||||
"aug": 0.0,
|
||||
"sep": 0.0,
|
||||
"oct": 0.0,
|
||||
"nov": 0.0,
|
||||
"dec": 0.0,
|
||||
"jan": 0.0,
|
||||
"feb": 0.0,
|
||||
"mar": 0.0,
|
||||
"total": 30.0
|
||||
},
|
||||
{
|
||||
"entity": "_Test Customer 2",
|
||||
"entity_name": "_Test Customer 2",
|
||||
"apr": 0.0,
|
||||
"may": 0.0,
|
||||
"jun": 0.0,
|
||||
"jul": 0.0,
|
||||
"aug": 0.0,
|
||||
"sep": 15.0,
|
||||
"oct": 10.0,
|
||||
"nov": 0.0,
|
||||
"dec": 0.0,
|
||||
"jan": 0.0,
|
||||
"feb": 0.0,
|
||||
"mar": 0.0,
|
||||
"apr_2017": 0.0,
|
||||
"may_2017": 0.0,
|
||||
"jun_2017": 0.0,
|
||||
"jul_2017": 0.0,
|
||||
"aug_2017": 0.0,
|
||||
"sep_2017": 15.0,
|
||||
"oct_2017": 10.0,
|
||||
"nov_2017": 0.0,
|
||||
"dec_2017": 0.0,
|
||||
"jan_2018": 0.0,
|
||||
"feb_2018": 0.0,
|
||||
"mar_2018": 0.0,
|
||||
"total":25.0
|
||||
},
|
||||
{
|
||||
"entity": "_Test Customer 3",
|
||||
"entity_name": "_Test Customer 3",
|
||||
"apr_2017": 0.0,
|
||||
"may_2017": 0.0,
|
||||
"jun_2017": 20.0,
|
||||
"jul_2017": 10.0,
|
||||
"aug_2017": 0.0,
|
||||
"sep_2017": 0.0,
|
||||
"oct_2017": 0.0,
|
||||
"nov_2017": 0.0,
|
||||
"dec_2017": 0.0,
|
||||
"jan_2018": 0.0,
|
||||
"feb_2018": 0.0,
|
||||
"mar_2018": 0.0,
|
||||
"total": 30.0
|
||||
}
|
||||
]
|
||||
self.assertEqual(expected_data, report[1])
|
||||
result = sorted(report[1], key=lambda k: k['entity'])
|
||||
self.assertEqual(expected_data, result)
|
||||
|
||||
def create_sales_order():
|
||||
def create_sales_orders():
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
make_sales_order(qty=10, customer = "_Test Customer 1", transaction_date='2018-02-10')
|
||||
make_sales_order(qty=10, customer = "_Test Customer 1", transaction_date='2018-02-15')
|
||||
make_sales_order(qty=15, customer = "_Test Customer 2", transaction_date='2017-09-23')
|
||||
make_sales_order(qty=10, customer = "_Test Customer 2", transaction_date='2017-10-10')
|
||||
make_sales_order(qty=20, customer = "_Test Customer 3", transaction_date='2017-06-15')
|
||||
make_sales_order(qty=10, customer = "_Test Customer 3", transaction_date='2017-07-10')
|
||||
make_sales_order(company="_Test Company 2", qty=10,
|
||||
customer = "_Test Customer 1",
|
||||
transaction_date = '2018-02-10',
|
||||
warehouse = 'Finished Goods - _TC2',
|
||||
currency = 'EUR')
|
||||
|
||||
make_sales_order(company="_Test Company 2",
|
||||
qty=10, customer = "_Test Customer 1",
|
||||
transaction_date = '2018-02-15',
|
||||
warehouse = 'Finished Goods - _TC2',
|
||||
currency = 'EUR')
|
||||
|
||||
make_sales_order(company = "_Test Company 2",
|
||||
qty=10, customer = "_Test Customer 2",
|
||||
transaction_date = '2017-10-10',
|
||||
warehouse='Finished Goods - _TC2',
|
||||
currency = 'EUR')
|
||||
|
||||
make_sales_order(company="_Test Company 2",
|
||||
qty=15, customer = "_Test Customer 2",
|
||||
transaction_date='2017-09-23',
|
||||
warehouse='Finished Goods - _TC2',
|
||||
currency = 'EUR')
|
||||
|
||||
make_sales_order(company="_Test Company 2",
|
||||
qty=20, customer = "_Test Customer 3",
|
||||
transaction_date='2017-06-15',
|
||||
warehouse='Finished Goods - _TC2',
|
||||
currency = 'EUR')
|
||||
|
||||
make_sales_order(company="_Test Company 2",
|
||||
qty=10, customer = "_Test Customer 3",
|
||||
transaction_date='2017-07-10',
|
||||
warehouse='Finished Goods - _TC2',
|
||||
currency = 'EUR')
|
||||
|
@ -97,7 +97,7 @@ class Company(NestedSet):
|
||||
|
||||
if not frappe.db.get_value("Department", {"company": self.name}):
|
||||
from erpnext.setup.setup_wizard.operations.install_fixtures import install_post_company_fixtures
|
||||
install_post_company_fixtures(self.name)
|
||||
install_post_company_fixtures(frappe._dict({'company_name': self.name}))
|
||||
|
||||
if not frappe.db.get_value("Cost Center", {"is_group": 0, "company": self.name}):
|
||||
self.create_default_cost_center()
|
||||
@ -363,7 +363,8 @@ def replace_abbr(company, old, new):
|
||||
for d in doc:
|
||||
_rename_record(d)
|
||||
|
||||
for dt in ["Warehouse", "Account", "Cost Center"]:
|
||||
for dt in ["Warehouse", "Account", "Cost Center", "Department", "Location",
|
||||
"Sales Taxes and Charges Template", "Purchase Taxes and Charges Template"]:
|
||||
_rename_records(dt)
|
||||
frappe.db.commit()
|
||||
|
||||
|
@ -6,6 +6,9 @@ from __future__ import unicode_literals
|
||||
import frappe, os, json
|
||||
|
||||
from frappe import _
|
||||
from frappe.desk.page.setup_wizard.setup_wizard import make_records
|
||||
from frappe.utils import cstr, getdate
|
||||
from erpnext.accounts.doctype.account.account import RootNotEditable
|
||||
|
||||
default_lead_sources = ["Existing Customer", "Reference", "Advertisement",
|
||||
"Cold Calling", "Exhibition", "Supplier Reference", "Mass Mailing",
|
||||
@ -26,34 +29,6 @@ def install(country=None):
|
||||
{ 'doctype': 'Domain', 'domain': 'Agriculture'},
|
||||
{ 'doctype': 'Domain', 'domain': 'Non Profit'},
|
||||
|
||||
# Setup Progress
|
||||
{'doctype': "Setup Progress", "actions": [
|
||||
{"action_name": "Add Company", "action_doctype": "Company", "min_doc_count": 1, "is_completed": 1,
|
||||
"domains": '[]' },
|
||||
{"action_name": "Set Sales Target", "action_doctype": "Company", "min_doc_count": 99,
|
||||
"action_document": frappe.defaults.get_defaults().get("company") or '',
|
||||
"action_field": "monthly_sales_target", "is_completed": 0,
|
||||
"domains": '["Manufacturing", "Services", "Retail", "Distribution"]' },
|
||||
{"action_name": "Add Customers", "action_doctype": "Customer", "min_doc_count": 1, "is_completed": 0,
|
||||
"domains": '["Manufacturing", "Services", "Retail", "Distribution"]' },
|
||||
{"action_name": "Add Suppliers", "action_doctype": "Supplier", "min_doc_count": 1, "is_completed": 0,
|
||||
"domains": '["Manufacturing", "Services", "Retail", "Distribution"]' },
|
||||
{"action_name": "Add Products", "action_doctype": "Item", "min_doc_count": 1, "is_completed": 0,
|
||||
"domains": '["Manufacturing", "Services", "Retail", "Distribution"]' },
|
||||
{"action_name": "Add Programs", "action_doctype": "Program", "min_doc_count": 1, "is_completed": 0,
|
||||
"domains": '["Education"]' },
|
||||
{"action_name": "Add Instructors", "action_doctype": "Instructor", "min_doc_count": 1, "is_completed": 0,
|
||||
"domains": '["Education"]' },
|
||||
{"action_name": "Add Courses", "action_doctype": "Course", "min_doc_count": 1, "is_completed": 0,
|
||||
"domains": '["Education"]' },
|
||||
{"action_name": "Add Rooms", "action_doctype": "Room", "min_doc_count": 1, "is_completed": 0,
|
||||
"domains": '["Education"]' },
|
||||
{"action_name": "Add Users", "action_doctype": "User", "min_doc_count": 4, "is_completed": 0,
|
||||
"domains": '[]' },
|
||||
{"action_name": "Add Letterhead", "action_doctype": "Letter Head", "min_doc_count": 1, "is_completed": 0,
|
||||
"domains": '[]' }
|
||||
]},
|
||||
|
||||
# address template
|
||||
{'doctype':"Address Template", "country": country},
|
||||
|
||||
@ -124,8 +99,10 @@ def install(country=None):
|
||||
{'doctype': 'Designation', 'designation_name': _('Designer')},
|
||||
{'doctype': 'Designation', 'designation_name': _('Researcher')},
|
||||
|
||||
# territory
|
||||
# territory: with two default territories, one for home country and one named Rest of the World
|
||||
{'doctype': 'Territory', 'territory_name': _('All Territories'), 'is_group': 1, 'name': _('All Territories'), 'parent_territory': ''},
|
||||
{'doctype': 'Territory', 'territory_name': country.replace("'", ""), 'is_group': 0, 'parent_territory': _('All Territories')},
|
||||
{'doctype': 'Territory', 'territory_name': _("Rest Of The World"), 'is_group': 0, 'parent_territory': _('All Territories')},
|
||||
|
||||
# customer group
|
||||
{'doctype': 'Customer Group', 'customer_group_name': _('All Customer Groups'), 'is_group': 1, 'name': _('All Customer Groups'), 'parent_customer_group': ''},
|
||||
@ -231,7 +208,22 @@ def install(country=None):
|
||||
|
||||
# Share Management
|
||||
{"doctype": "Share Type", "title": _("Equity")},
|
||||
{"doctype": "Share Type", "title": _("Preference")}
|
||||
{"doctype": "Share Type", "title": _("Preference")},
|
||||
|
||||
# Market Segments
|
||||
{"doctype": "Market Segment", "market_segment": _("Lower Income")},
|
||||
{"doctype": "Market Segment", "market_segment": _("Middle Income")},
|
||||
{"doctype": "Market Segment", "market_segment": _("Upper Income")},
|
||||
|
||||
# Sales Stages
|
||||
{"doctype": "Sales Stage", "stage_name": _("Prospecting")},
|
||||
{"doctype": "Sales Stage", "stage_name": _("Qualification")},
|
||||
{"doctype": "Sales Stage", "stage_name": _("Needs Analysis")},
|
||||
{"doctype": "Sales Stage", "stage_name": _("Value Proposition")},
|
||||
{"doctype": "Sales Stage", "stage_name": _("Identifying Decision Makers")},
|
||||
{"doctype": "Sales Stage", "stage_name": _("Perception Analysis")},
|
||||
{"doctype": "Sales Stage", "stage_name": _("Proposal/Price Quote")},
|
||||
{"doctype": "Sales Stage", "stage_name": _("Negotiation/Review")}
|
||||
]
|
||||
|
||||
from erpnext.setup.setup_wizard.data.industry_type import get_industry_types
|
||||
@ -260,7 +252,17 @@ def install(country=None):
|
||||
from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import make_default_records
|
||||
make_default_records()
|
||||
|
||||
make_fixture_records(records)
|
||||
make_records(records, True)
|
||||
|
||||
set_more_defaults()
|
||||
|
||||
# path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(country))
|
||||
# if os.path.exists(path.encode("utf-8")):
|
||||
# frappe.get_attr("erpnext.regional.{0}.setup.setup_company_independent_fixtures".format(frappe.scrub(country)))()
|
||||
|
||||
|
||||
def set_more_defaults():
|
||||
# Do more setup stuff that can be done here with no dependencies
|
||||
|
||||
# set default customer group and territory
|
||||
selling_settings = frappe.get_doc("Selling Settings")
|
||||
@ -269,6 +271,33 @@ def install(country=None):
|
||||
|
||||
add_uom_data()
|
||||
|
||||
# set no copy fields of an item doctype to item variant settings
|
||||
doc = frappe.get_doc('Item Variant Settings')
|
||||
doc.set_default_fields()
|
||||
doc.save()
|
||||
|
||||
selling_settings = frappe.get_doc("Selling Settings")
|
||||
selling_settings.cust_master_name = "Customer Name"
|
||||
selling_settings.so_required = "No"
|
||||
selling_settings.dn_required = "No"
|
||||
selling_settings.allow_multiple_items = 1
|
||||
selling_settings.sales_update_frequency = "Each Transaction"
|
||||
selling_settings.save()
|
||||
|
||||
buying_settings = frappe.get_doc("Buying Settings")
|
||||
buying_settings.supp_master_name = "Supplier Name"
|
||||
buying_settings.po_required = "No"
|
||||
buying_settings.pr_required = "No"
|
||||
buying_settings.maintain_same_rate = 1
|
||||
buying_settings.allow_multiple_items = 1
|
||||
buying_settings.save()
|
||||
|
||||
hr_settings = frappe.get_doc("HR Settings")
|
||||
hr_settings.emp_created_by = "Naming Series"
|
||||
hr_settings.leave_approval_notification_template = _("Leave Approval Notification")
|
||||
hr_settings.leave_status_notification_template = _("Leave Status Notification")
|
||||
hr_settings.save()
|
||||
|
||||
def add_uom_data():
|
||||
# add UOMs
|
||||
uoms = json.loads(open(frappe.get_app_path("erpnext", "setup", "setup_wizard", "data", "uom_data.json")).read())
|
||||
@ -306,7 +335,7 @@ def add_market_segments():
|
||||
{"doctype": "Market Segment", "market_segment": _("Upper Income")}
|
||||
]
|
||||
|
||||
make_fixture_records(records)
|
||||
make_records(records)
|
||||
|
||||
def add_sale_stages():
|
||||
# Sale Stages
|
||||
@ -320,46 +349,143 @@ def add_sale_stages():
|
||||
{"doctype": "Sales Stage", "stage_name": _("Proposal/Price Quote")},
|
||||
{"doctype": "Sales Stage", "stage_name": _("Negotiation/Review")}
|
||||
]
|
||||
make_fixture_records(records)
|
||||
|
||||
def make_fixture_records(records):
|
||||
from frappe.modules import scrub
|
||||
for r in records:
|
||||
doc = frappe.new_doc(r.get("doctype"))
|
||||
doc.update(r)
|
||||
make_records(records)
|
||||
|
||||
# ignore mandatory for root
|
||||
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
|
||||
def install_company(args):
|
||||
records = [
|
||||
# Fiscal Year
|
||||
{ "doctype": "Fiscal Year", 'year': get_fy_details(args.fy_start_date, args.fy_end_date), 'year_start_date': args.fy_start_date, 'year_end_date': args.fy_end_date },
|
||||
|
||||
try:
|
||||
doc.insert(ignore_permissions=True)
|
||||
except frappe.DuplicateEntryError as e:
|
||||
# pass DuplicateEntryError and continue
|
||||
if e.args and e.args[0]==doc.doctype and e.args[1]==doc.name:
|
||||
# make sure DuplicateEntryError is for the exact same doc and not a related doc
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
# Company
|
||||
{
|
||||
"doctype":"Company",
|
||||
'company_name': args.company_name,
|
||||
'enable_perpetual_inventory': 1,
|
||||
'abbr': args.company_abbr,
|
||||
'default_currency': args.currency,
|
||||
'country': args.country,
|
||||
'create_chart_of_accounts_based_on': 'Standard Template',
|
||||
'chart_of_accounts': args.chart_of_accounts,
|
||||
'domain': args.domain
|
||||
}
|
||||
]
|
||||
|
||||
def install_post_company_fixtures(company=None):
|
||||
make_records(records, True)
|
||||
|
||||
|
||||
def install_post_company_fixtures(args=None):
|
||||
records = [
|
||||
# Department
|
||||
{'doctype': 'Department', 'department_name': _('All Departments'), 'is_group': 1, 'parent_department': ''},
|
||||
{'doctype': 'Department', 'department_name': _('Accounts'), 'parent_department': _('All Departments'), 'company': company},
|
||||
{'doctype': 'Department', 'department_name': _('Marketing'), 'parent_department': _('All Departments'), 'company': company},
|
||||
{'doctype': 'Department', 'department_name': _('Sales'), 'parent_department': _('All Departments'), 'company': company},
|
||||
{'doctype': 'Department', 'department_name': _('Purchase'), 'parent_department': _('All Departments'), 'company': company},
|
||||
{'doctype': 'Department', 'department_name': _('Operations'), 'parent_department': _('All Departments'), 'company': company},
|
||||
{'doctype': 'Department', 'department_name': _('Production'), 'parent_department': _('All Departments'), 'company': company},
|
||||
{'doctype': 'Department', 'department_name': _('Dispatch'), 'parent_department': _('All Departments'), 'company': company},
|
||||
{'doctype': 'Department', 'department_name': _('Customer Service'), 'parent_department': _('All Departments'), 'company': company},
|
||||
{'doctype': 'Department', 'department_name': _('Human Resources'), 'parent_department': _('All Departments'), 'company': company},
|
||||
{'doctype': 'Department', 'department_name': _('Management'), 'parent_department': _('All Departments'), 'company': company},
|
||||
{'doctype': 'Department', 'department_name': _('Quality Management'), 'parent_department': _('All Departments'), 'company': company},
|
||||
{'doctype': 'Department', 'department_name': _('Research & Development'), 'parent_department': _('All Departments'), 'company': company},
|
||||
{'doctype': 'Department', 'department_name': _('Legal'), 'parent_department': _('All Departments'), 'company': company},
|
||||
{'doctype': 'Department', 'department_name': _('Accounts'), 'parent_department': _('All Departments'), 'company': args.company_name},
|
||||
{'doctype': 'Department', 'department_name': _('Marketing'), 'parent_department': _('All Departments'), 'company': args.company_name},
|
||||
{'doctype': 'Department', 'department_name': _('Sales'), 'parent_department': _('All Departments'), 'company': args.company_name},
|
||||
{'doctype': 'Department', 'department_name': _('Purchase'), 'parent_department': _('All Departments'), 'company': args.company_name},
|
||||
{'doctype': 'Department', 'department_name': _('Operations'), 'parent_department': _('All Departments'), 'company': args.company_name},
|
||||
{'doctype': 'Department', 'department_name': _('Production'), 'parent_department': _('All Departments'), 'company': args.company_name},
|
||||
{'doctype': 'Department', 'department_name': _('Dispatch'), 'parent_department': _('All Departments'), 'company': args.company_name},
|
||||
{'doctype': 'Department', 'department_name': _('Customer Service'), 'parent_department': _('All Departments'), 'company': args.company_name},
|
||||
{'doctype': 'Department', 'department_name': _('Human Resources'), 'parent_department': _('All Departments'), 'company': args.company_name},
|
||||
{'doctype': 'Department', 'department_name': _('Management'), 'parent_department': _('All Departments'), 'company': args.company_name},
|
||||
{'doctype': 'Department', 'department_name': _('Quality Management'), 'parent_department': _('All Departments'), 'company': args.company_name},
|
||||
{'doctype': 'Department', 'department_name': _('Research & Development'), 'parent_department': _('All Departments'), 'company': args.company_name},
|
||||
{'doctype': 'Department', 'department_name': _('Legal'), 'parent_department': _('All Departments'), 'company': args.company_name},
|
||||
]
|
||||
|
||||
make_fixture_records(records)
|
||||
make_records(records)
|
||||
|
||||
|
||||
def install_defaults(args=None):
|
||||
records = [
|
||||
# Price Lists
|
||||
{ "doctype": "Price List", "price_list_name": _("Standard Buying"), "enabled": 1, "buying": 1, "selling": 0, "currency": args.currency },
|
||||
{ "doctype": "Price List", "price_list_name": _("Standard Selling"), "enabled": 1, "buying": 0, "selling": 1, "currency": args.currency },
|
||||
]
|
||||
|
||||
make_records(records)
|
||||
|
||||
# enable default currency
|
||||
frappe.db.set_value("Currency", args.get("currency"), "enabled", 1)
|
||||
|
||||
global_defaults = frappe.get_doc("Global Defaults", "Global Defaults")
|
||||
current_fiscal_year = frappe.get_all("Fiscal Year")[0]
|
||||
|
||||
global_defaults.update({
|
||||
'current_fiscal_year': current_fiscal_year.name,
|
||||
'default_currency': args.get('currency'),
|
||||
'default_company':args.get('company_name') ,
|
||||
"country": args.get("country"),
|
||||
})
|
||||
|
||||
global_defaults.save()
|
||||
|
||||
system_settings = frappe.get_doc("System Settings")
|
||||
system_settings.email_footer_address = args.get("company_name")
|
||||
system_settings.save()
|
||||
|
||||
domain_settings = frappe.get_single('Domain Settings')
|
||||
domain_settings.set_active_domains(args.get('domains'))
|
||||
|
||||
stock_settings = frappe.get_doc("Stock Settings")
|
||||
stock_settings.item_naming_by = "Item Code"
|
||||
stock_settings.valuation_method = "FIFO"
|
||||
stock_settings.default_warehouse = frappe.db.get_value('Warehouse', {'warehouse_name': _('Stores')})
|
||||
stock_settings.stock_uom = _("Nos")
|
||||
stock_settings.auto_indent = 1
|
||||
stock_settings.auto_insert_price_list_rate_if_missing = 1
|
||||
stock_settings.automatically_set_serial_nos_based_on_fifo = 1
|
||||
stock_settings.set_qty_in_transactions_based_on_serial_no_input = 1
|
||||
stock_settings.save()
|
||||
|
||||
if args.bank_account:
|
||||
company_name = args.company_name
|
||||
bank_account_group = frappe.db.get_value("Account",
|
||||
{"account_type": "Bank", "is_group": 1, "root_type": "Asset",
|
||||
"company": company_name})
|
||||
if bank_account_group:
|
||||
bank_account = frappe.get_doc({
|
||||
"doctype": "Account",
|
||||
'account_name': args.bank_account,
|
||||
'parent_account': bank_account_group,
|
||||
'is_group':0,
|
||||
'company': company_name,
|
||||
"account_type": "Bank",
|
||||
})
|
||||
try:
|
||||
doc = bank_account.insert()
|
||||
|
||||
frappe.db.set_value("Company", args.company_name, "default_bank_account", bank_account.name, update_modified=False)
|
||||
|
||||
return doc
|
||||
except RootNotEditable:
|
||||
frappe.throw(_("Bank account cannot be named as {0}").format(args.bank_account))
|
||||
except frappe.DuplicateEntryError:
|
||||
# bank account same as a CoA entry
|
||||
pass
|
||||
|
||||
# Now, with fixtures out of the way, onto concrete stuff
|
||||
records = [
|
||||
|
||||
# Shopping cart: needs price lists
|
||||
{
|
||||
"doctype": "Shopping Cart Settings",
|
||||
"enabled": 1,
|
||||
'company': args.company_name,
|
||||
# uh oh
|
||||
'price_list': frappe.db.get_value("Price List", {"selling": 1}),
|
||||
'default_customer_group': _("Individual"),
|
||||
'quotation_series': "QTN-",
|
||||
},
|
||||
]
|
||||
|
||||
make_records(records, True)
|
||||
|
||||
|
||||
def get_fy_details(fy_start_date, fy_end_date):
|
||||
start_year = getdate(fy_start_date).year
|
||||
if start_year == getdate(fy_end_date).year:
|
||||
fy = cstr(start_year)
|
||||
else:
|
||||
fy = cstr(start_year) + '-' + cstr(start_year + 1)
|
||||
return fy
|
||||
|
@ -5,7 +5,8 @@ from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from .operations import install_fixtures, taxes_setup, defaults_setup, company_setup, sample_data
|
||||
|
||||
from .operations import install_fixtures as fixtures, company_setup, taxes_setup, sample_data
|
||||
|
||||
def get_setup_stages(args=None):
|
||||
if frappe.db.sql("select name from tabCompany"):
|
||||
@ -61,16 +62,10 @@ def get_setup_stages(args=None):
|
||||
'fail_msg': _("Failed to setup post company fixtures")
|
||||
},
|
||||
{
|
||||
'fn': stage_three,
|
||||
'fn': setup_defaults,
|
||||
'args': args,
|
||||
'fail_msg': _("Failed to set defaults")
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'status': _('Making website'),
|
||||
'fail_msg': _('Failed to create website'),
|
||||
'tasks': [
|
||||
'fail_msg': _("Failed to setup defaults")
|
||||
},
|
||||
{
|
||||
'fn': stage_four,
|
||||
'args': args,
|
||||
@ -93,38 +88,20 @@ def get_setup_stages(args=None):
|
||||
|
||||
return stages
|
||||
|
||||
def setup_complete(args=None):
|
||||
stage_fixtures(args)
|
||||
setup_company(args)
|
||||
setup_taxes(args)
|
||||
setup_post_company_fixtures(args)
|
||||
stage_three(args)
|
||||
stage_four(args)
|
||||
fin(args)
|
||||
|
||||
def stage_fixtures(args):
|
||||
install_fixtures.install(args.get("country"))
|
||||
install_fixtures.add_market_segments()
|
||||
install_fixtures.add_sale_stages()
|
||||
fixtures.install(args.get('country'))
|
||||
|
||||
def setup_company(args):
|
||||
defaults_setup.create_price_lists(args)
|
||||
company_setup.create_fiscal_year_and_company(args)
|
||||
company_setup.enable_shopping_cart(args)
|
||||
company_setup.create_bank_account(args)
|
||||
fixtures.install_company(args)
|
||||
|
||||
def setup_taxes(args):
|
||||
taxes_setup.create_sales_tax(args)
|
||||
|
||||
def setup_post_company_fixtures(args):
|
||||
install_fixtures.install_post_company_fixtures(args.get("company_name"))
|
||||
fixtures.install_post_company_fixtures(args)
|
||||
|
||||
def stage_three(args):
|
||||
defaults_setup.create_employee_for_self(args)
|
||||
defaults_setup.set_default_settings(args)
|
||||
defaults_setup.create_territories()
|
||||
defaults_setup.create_feed_and_todo()
|
||||
defaults_setup.set_no_copy_fields_in_variant_settings()
|
||||
def setup_defaults(args):
|
||||
fixtures.install_defaults(frappe._dict(args))
|
||||
|
||||
def stage_four(args):
|
||||
company_setup.create_website(args)
|
||||
@ -149,3 +126,14 @@ def make_sample_data(domains):
|
||||
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"))
|
||||
|
||||
|
||||
# Only for programmatical use
|
||||
def setup_complete(args=None):
|
||||
stage_fixtures(args)
|
||||
setup_company(args)
|
||||
setup_taxes(args)
|
||||
setup_post_company_fixtures(args)
|
||||
setup_defaults(args)
|
||||
stage_four(args)
|
||||
fin(args)
|
||||
|
@ -223,6 +223,10 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
|
||||
erpnext.setup_serial_no();
|
||||
},
|
||||
|
||||
packed_items_on_form_rendered: function(doc, grid_row) {
|
||||
erpnext.setup_serial_no();
|
||||
},
|
||||
|
||||
close_delivery_note: function(doc){
|
||||
this.update_status("Closed")
|
||||
},
|
||||
|
@ -1,5 +1,5 @@
|
||||
frappe.listview_settings['Delivery Note'] = {
|
||||
add_fields: ["grand_total", "is_return", "per_billed", "status"],
|
||||
add_fields: ["grand_total", "is_return", "per_billed", "status", "currency"],
|
||||
get_indicator: function (doc) {
|
||||
if (cint(doc.is_return) == 1) {
|
||||
return [__("Return"), "darkgrey", "is_return,=,Yes"];
|
||||
|
@ -513,7 +513,7 @@ $.extend(erpnext.item, {
|
||||
fields: ["attribute_value"],
|
||||
limit_start: 0,
|
||||
limit_page_length: 500,
|
||||
parent: "Item",
|
||||
parent: "Item Attribute",
|
||||
order_by: "idx"
|
||||
}
|
||||
}).then((r) => {
|
||||
|
@ -32,6 +32,10 @@ class StockExistsForTemplate(frappe.ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidBarcode(frappe.ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class Item(WebsiteGenerator):
|
||||
website = frappe._dict(
|
||||
page_title_field="item_name",
|
||||
@ -502,19 +506,19 @@ class Item(WebsiteGenerator):
|
||||
from stdnum import ean
|
||||
if len(self.barcodes) > 0:
|
||||
for item_barcode in self.barcodes:
|
||||
options = frappe.get_meta("Item Barcode").get_options("barcode_type").split()
|
||||
options = frappe.get_meta("Item Barcode").get_options("barcode_type").split('\n')
|
||||
if item_barcode.barcode:
|
||||
duplicate = frappe.db.sql(
|
||||
"""select parent from `tabItem Barcode` where barcode = %s and parent != %s""", (item_barcode.barcode, self.name))
|
||||
if duplicate:
|
||||
frappe.throw(_("Barcode {0} already used in Item {1}").format(
|
||||
item_barcode.barcode, duplicate[0][0]))
|
||||
item_barcode.barcode, duplicate[0][0]), frappe.DuplicateEntryError)
|
||||
|
||||
item_barcode.barcode_type = "" if item_barcode.barcode_type not in options else item_barcode.barcode_type
|
||||
if item_barcode.barcode_type:
|
||||
if item_barcode.barcode_type and item_barcode.barcode_type.upper() in ('EAN', 'UPC-A', 'EAN-13', 'EAN-8'):
|
||||
if not ean.is_valid(item_barcode.barcode):
|
||||
frappe.throw(_("Barcode {0} is not a valid {1} code").format(
|
||||
item_barcode.barcode, item_barcode.barcode_type))
|
||||
item_barcode.barcode, item_barcode.barcode_type), InvalidBarcode)
|
||||
|
||||
def validate_warehouse_for_reorder(self):
|
||||
'''Validate Reorder level table for duplicate and conditional mandatory'''
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user