Compare commits
126 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
185f488c51 | ||
|
|
f3006972d5 | ||
|
|
dccc6bc11d | ||
|
|
15d2c89939 | ||
|
|
ca4c8a2a46 | ||
|
|
c320fe541d | ||
|
|
f40a87511e | ||
|
|
4945b94950 | ||
|
|
cb96b61449 | ||
|
|
248c867a2c | ||
|
|
da98ab6f3c | ||
|
|
cd0989e051 | ||
|
|
b20baf894f | ||
|
|
d3cf4f1264 | ||
|
|
2dbe2b63b2 | ||
|
|
edba8f5582 | ||
|
|
89b8d11f9c | ||
|
|
19a33994da | ||
|
|
89349d3ae3 | ||
|
|
f6a31a568a | ||
|
|
4bcbcd29f7 | ||
|
|
740a11263f | ||
|
|
142859f36e | ||
|
|
6534ad082d | ||
|
|
6361ae3495 | ||
|
|
7f75dbf061 | ||
|
|
199d8a44fc | ||
|
|
72b1128467 | ||
|
|
d6cb55ad1a | ||
|
|
c76e34d7de | ||
|
|
33b10faf94 | ||
|
|
d970b001a4 | ||
|
|
d767fb6134 | ||
|
|
6239923340 | ||
|
|
e5a31462fe | ||
|
|
6f39300d43 | ||
|
|
0ca587e018 | ||
|
|
8ffe12ebe4 | ||
|
|
8579af371c | ||
|
|
b5ff9421e1 | ||
|
|
ba8ec17f0b | ||
|
|
44bd3b2601 | ||
|
|
9d40eca428 | ||
|
|
1b6c00e2c7 | ||
|
|
52efde31e7 | ||
|
|
f723032fd7 | ||
|
|
3297c43bdf | ||
|
|
aea250bc5a | ||
|
|
d37d4dfdec | ||
|
|
0ea68b33ed | ||
|
|
7f96c20f5b | ||
|
|
e75d947867 | ||
|
|
bba0a5d38f | ||
|
|
953e97536a | ||
|
|
e74d7ca33e | ||
|
|
8f2b8afcb7 | ||
|
|
b3f12c3109 | ||
|
|
f66e6aacd4 | ||
|
|
1d621be1f7 | ||
|
|
ab57e52cbd | ||
|
|
e63da9813c | ||
|
|
c020e42d20 | ||
|
|
b126ba0132 | ||
|
|
20ae349ebd | ||
|
|
c6e4b5978d | ||
|
|
361eca4cae | ||
|
|
9fc03b6c10 | ||
|
|
e2d46d0474 | ||
|
|
dd2f2f5321 | ||
|
|
6e4f5a214a | ||
|
|
b07b0a9e54 | ||
|
|
307978fea9 | ||
|
|
06ad308ca1 | ||
|
|
52dfc32eca | ||
|
|
72fbf902d7 | ||
|
|
8ffd483e24 | ||
|
|
3d76686b82 | ||
|
|
156ce607e2 | ||
|
|
65b6762247 | ||
|
|
abdfb4d3db | ||
|
|
3bcb13b1b8 | ||
|
|
b6ec680c46 | ||
|
|
53a0de7607 | ||
|
|
0a1d037f01 | ||
|
|
ce3e15d30c | ||
|
|
5b649521d1 | ||
|
|
798e75832c | ||
|
|
aa95a1b1ef | ||
|
|
c0c94aef44 | ||
|
|
e4dfeb651f | ||
|
|
f7e6eb4fd7 | ||
|
|
97c858a5e3 | ||
|
|
974892bf87 | ||
|
|
361df8993f | ||
|
|
be6cfddc4d | ||
|
|
bf8c8df9ce | ||
|
|
26bcd89d10 | ||
|
|
9acd6a2629 | ||
|
|
779ae439cd | ||
|
|
a8ef4c9220 | ||
|
|
0332f83bc2 | ||
|
|
926ae17e5a | ||
|
|
5532a14938 | ||
|
|
e9b14e497b | ||
|
|
cba5a684cb | ||
|
|
340709b2da | ||
|
|
28386f551b | ||
|
|
9ff52cd57e | ||
|
|
e3401182c8 | ||
|
|
0bd145a608 | ||
|
|
c663f5c2bd | ||
|
|
adde1cff48 | ||
|
|
bb9427d1ef | ||
|
|
052babc6b6 | ||
|
|
24fa06bc53 | ||
|
|
7c867ae9ad | ||
|
|
8f7317175d | ||
|
|
957e7a37be | ||
|
|
def71d4d5d | ||
|
|
ef511b160e | ||
|
|
09f9c96c53 | ||
|
|
1a2d121073 | ||
|
|
810bd35609 | ||
|
|
949d7dbaba | ||
|
|
72e6aa160c | ||
|
|
deb38f7a68 |
@@ -1,2 +1,2 @@
|
||||
from __future__ import unicode_literals
|
||||
__version__ = '6.2.0'
|
||||
__version__ = '6.4.0'
|
||||
|
||||
@@ -50,20 +50,20 @@ class Account(Document):
|
||||
def set_root_and_report_type(self):
|
||||
if self.parent_account:
|
||||
par = frappe.db.get_value("Account", self.parent_account, ["report_type", "root_type"], as_dict=1)
|
||||
|
||||
|
||||
if par.report_type:
|
||||
self.report_type = par.report_type
|
||||
if par.root_type:
|
||||
self.root_type = par.root_type
|
||||
|
||||
|
||||
if self.is_group:
|
||||
db_value = frappe.db.get_value("Account", self.name, ["report_type", "root_type"], as_dict=1)
|
||||
if db_value:
|
||||
if self.report_type != db_value.report_type:
|
||||
frappe.db.sql("update `tabAccount` set report_type=%s where lft > %s and rgt < %s",
|
||||
frappe.db.sql("update `tabAccount` set report_type=%s where lft > %s and rgt < %s",
|
||||
(self.report_type, self.lft, self.rgt))
|
||||
if self.root_type != db_value.root_type:
|
||||
frappe.db.sql("update `tabAccount` set root_type=%s where lft > %s and rgt < %s",
|
||||
frappe.db.sql("update `tabAccount` set root_type=%s where lft > %s and rgt < %s",
|
||||
(self.root_type, self.lft, self.rgt))
|
||||
|
||||
def validate_root_details(self):
|
||||
@@ -89,11 +89,11 @@ class Account(Document):
|
||||
frappe.throw(_("Account balance already in Debit, you are not allowed to set 'Balance Must Be' as 'Credit'"))
|
||||
elif account_balance < 0 and self.balance_must_be == "Debit":
|
||||
frappe.throw(_("Account balance already in Credit, you are not allowed to set 'Balance Must Be' as 'Debit'"))
|
||||
|
||||
|
||||
def validate_account_currency(self):
|
||||
if not self.account_currency:
|
||||
self.account_currency = frappe.db.get_value("Company", self.company, "default_currency")
|
||||
|
||||
|
||||
elif self.account_currency != frappe.db.get_value("Account", self.name, "account_currency"):
|
||||
if frappe.db.get_value("GL Entry", {"account": self.name}):
|
||||
frappe.throw(_("Currency can not be changed after making entries using some other currency"))
|
||||
@@ -207,3 +207,14 @@ def get_parent_account(doctype, txt, searchfield, start, page_len, filters):
|
||||
and %s like %s order by name limit %s, %s""" %
|
||||
("%s", searchfield, "%s", "%s", "%s"),
|
||||
(filters["company"], "%%%s%%" % txt, start, page_len), as_list=1)
|
||||
|
||||
def get_account_currency(account):
|
||||
"""Helper function to get account currency"""
|
||||
def generator():
|
||||
account_currency, company = frappe.db.get_value("Account", account, ["account_currency", "company"])
|
||||
if not account_currency:
|
||||
account_currency = frappe.db.get_value("Company", company, "default_currency")
|
||||
|
||||
return account_currency
|
||||
|
||||
return frappe.local_cache("account_currency", account, generator)
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
{
|
||||
"country_code": "gt",
|
||||
"name": "Cuentas de Guatemala",
|
||||
"is_active": "Yes",
|
||||
"tree": {
|
||||
"Activos": {
|
||||
"Activo Corriente": {
|
||||
"Caja y Bancos": {},
|
||||
"Cuentas por Cobrar": {},
|
||||
"Impuestos por Cobrar": {
|
||||
"IVA por Cobrar": {},
|
||||
"Retenciones de IVA recibidas": {}
|
||||
},
|
||||
"Inventario": {}
|
||||
},
|
||||
"No Corriente": {
|
||||
"Activos Fijos": {},
|
||||
"Cargos Diferidos": {}
|
||||
},
|
||||
"root_type": "Asset"
|
||||
},
|
||||
"Pasivos": {
|
||||
"Pasivo Corriente": {
|
||||
"Proveedores": {
|
||||
"Inventario Recibido pero No Cobrado": {
|
||||
"account_type": "Stock Received But Not Billed"
|
||||
}
|
||||
},
|
||||
"Impuestos por Pagar": {},
|
||||
"Sueldos por Liquidar": {},
|
||||
"Prestaciones": {},
|
||||
"Cuentas por Pagar": {},
|
||||
"Otras Cuentas por Pagar": {},
|
||||
"Acreedores": {}
|
||||
},
|
||||
"Pasivo No Corriente": {
|
||||
"Provisión para Indemnizaciones": {},
|
||||
"Acreedores": {}
|
||||
},
|
||||
"root_type": "Liability"
|
||||
},
|
||||
"Patrimonio": {
|
||||
"Capital": {},
|
||||
"Utilidades Retenidas": {},
|
||||
"Resultados del Ejercicio": {},
|
||||
"root_type": "Asset"
|
||||
},
|
||||
"Costos": {
|
||||
"Costo de Ventas": {},
|
||||
"Costos Incluidos en la Valuación": {
|
||||
"account_type": "Expenses Included In Valuation"
|
||||
},
|
||||
"Stock Adjustment": {
|
||||
"account_type": "Stock Adjustment"
|
||||
},
|
||||
"root_type": "Expense"
|
||||
},
|
||||
"Gastos": {
|
||||
"Gastos de Personal": {},
|
||||
"Honorarios Profesionales": {},
|
||||
"Servicios Básicos": {},
|
||||
"Alquileres": {},
|
||||
"Seguros": {},
|
||||
"Mantenimiento": {},
|
||||
"Depreciaciones": {},
|
||||
"Gastos Diversos": {},
|
||||
"root_type": "Expense"
|
||||
},
|
||||
"Ingresos": {
|
||||
"Productos": {},
|
||||
"Servicios": {},
|
||||
"root_type": "Income"
|
||||
},
|
||||
"Otros Gastos y Productos Financieros": {
|
||||
"Otros Ingresos": {
|
||||
"Otros Gastos y Productos Financieros": {
|
||||
"Intereses": {},
|
||||
"Otros Gastos Financieros": {}
|
||||
}
|
||||
},
|
||||
"Otros Gastos": {
|
||||
"Otros Gastos y Productos Financieros": {
|
||||
"Intereses": {},
|
||||
"Otros Gastos Financieros": {}
|
||||
}
|
||||
},
|
||||
"root_type": "Expense"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -330,7 +330,7 @@
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"search_index": 1,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
@@ -511,7 +511,7 @@
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-09-11 15:51:26",
|
||||
"modified": "2015-09-21 15:51:26",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "GL Entry",
|
||||
|
||||
@@ -3,15 +3,13 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
from frappe.utils import flt, fmt_money, getdate, formatdate
|
||||
from frappe import _
|
||||
|
||||
from frappe.utils import flt, fmt_money, getdate, formatdate
|
||||
from frappe.model.document import Document
|
||||
|
||||
class CustomerFrozen(frappe.ValidationError): pass
|
||||
class InvalidCurrency(frappe.ValidationError): pass
|
||||
class InvalidAccountCurrency(frappe.ValidationError): pass
|
||||
from erpnext.accounts.party import validate_party_gle_currency, get_party_account_currency
|
||||
from erpnext.accounts.utils import get_account_currency
|
||||
from erpnext.setup.doctype.company.company import get_company_currency
|
||||
from erpnext.exceptions import InvalidAccountCurrency, CustomerFrozen
|
||||
|
||||
class GLEntry(Document):
|
||||
def validate(self):
|
||||
@@ -101,25 +99,26 @@ class GLEntry(Document):
|
||||
if not frozen_accounts_modifier in frappe.get_roles():
|
||||
if frappe.db.get_value(self.party_type, self.party, "is_frozen"):
|
||||
frappe.throw("{0} {1} is frozen".format(self.party_type, self.party), CustomerFrozen)
|
||||
|
||||
|
||||
def validate_currency(self):
|
||||
company_currency = frappe.db.get_value("Company", self.company, "default_currency")
|
||||
account_currency = frappe.db.get_value("Account", self.account, "account_currency") or company_currency
|
||||
company_currency = get_company_currency(self.company)
|
||||
account_currency = get_account_currency(self.account)
|
||||
|
||||
if not self.account_currency:
|
||||
self.account_currency = company_currency
|
||||
|
||||
if account_currency != self.account_currency:
|
||||
frappe.throw(_("Accounting Entry for {0} can only be made in currency: {1}")
|
||||
.format(self.account, (account_currency or company_currency)), InvalidAccountCurrency)
|
||||
|
||||
|
||||
|
||||
if self.party_type and self.party:
|
||||
party_account_currency = frappe.db.get_value(self.party_type, self.party, "party_account_currency") \
|
||||
or company_currency
|
||||
party_account_currency = get_party_account_currency(self.party_type, self.party, self.company)
|
||||
|
||||
if party_account_currency != self.account_currency:
|
||||
frappe.throw(_("Accounting Entry for {0}: {1} can only be made in currency: {2}")
|
||||
.format(self.party_type, self.party, party_account_currency), InvalidAccountCurrency)
|
||||
.format(self.party_type, self.party, party_account_currency), InvalidAccountCurrency)
|
||||
|
||||
validate_party_gle_currency(self.party_type, self.party, self.company)
|
||||
|
||||
def validate_balance_type(account, adv_adj=False):
|
||||
if not adv_adj and account:
|
||||
@@ -159,7 +158,7 @@ def update_outstanding_amt(account, party_type, party, against_voucher_type, aga
|
||||
where against_voucher_type=%s and against_voucher=%s
|
||||
and account = %s {0}""".format(party_condition),
|
||||
(against_voucher_type, against_voucher, account))[0][0] or 0.0)
|
||||
|
||||
|
||||
if against_voucher_type == 'Purchase Invoice':
|
||||
bal = -bal
|
||||
elif against_voucher_type == "Journal Entry":
|
||||
|
||||
@@ -6,7 +6,8 @@ import frappe
|
||||
from frappe.utils import cstr, flt, fmt_money, formatdate
|
||||
from frappe import msgprint, _, scrub
|
||||
from erpnext.controllers.accounts_controller import AccountsController
|
||||
from erpnext.accounts.utils import get_balance_on
|
||||
from erpnext.accounts.utils import get_balance_on, get_account_currency
|
||||
from erpnext.accounts.party import get_party_account_currency
|
||||
from erpnext.setup.utils import get_company_currency
|
||||
|
||||
|
||||
@@ -146,7 +147,7 @@ class JournalEntry(AccountsController):
|
||||
|
||||
self.reference_totals = {}
|
||||
self.reference_types = {}
|
||||
self.reference_parties = {}
|
||||
self.reference_accounts = {}
|
||||
|
||||
for d in self.get("accounts"):
|
||||
if not d.reference_type:
|
||||
@@ -169,8 +170,7 @@ class JournalEntry(AccountsController):
|
||||
self.reference_totals[d.reference_name] = 0.0
|
||||
self.reference_totals[d.reference_name] += flt(d.get(dr_or_cr))
|
||||
self.reference_types[d.reference_name] = d.reference_type
|
||||
if d.party_type and d.party:
|
||||
self.reference_parties[d.reference_name] = [d.party_type, d.party]
|
||||
self.reference_accounts[d.reference_name] = d.account
|
||||
|
||||
against_voucher = frappe.db.get_value(d.reference_type, d.reference_name,
|
||||
[scrub(dt) for dt in field_dict.get(d.reference_type)])
|
||||
@@ -196,7 +196,7 @@ class JournalEntry(AccountsController):
|
||||
"""Validate totals, stopped and docstatus for orders"""
|
||||
for reference_name, total in self.reference_totals.iteritems():
|
||||
reference_type = self.reference_types[reference_name]
|
||||
party_type, party = self.reference_parties.get(reference_name)
|
||||
account = self.reference_accounts[reference_name]
|
||||
|
||||
if reference_type in ("Sales Order", "Purchase Order"):
|
||||
order = frappe.db.get_value(reference_type, reference_name,
|
||||
@@ -212,8 +212,8 @@ class JournalEntry(AccountsController):
|
||||
if cstr(order.status) == "Stopped":
|
||||
frappe.throw(_("{0} {1} is stopped").format(reference_type, reference_name))
|
||||
|
||||
party_account_currency = frappe.db.get_value(party_type, party, "party_account_currency")
|
||||
if party_account_currency == self.company_currency:
|
||||
account_currency = get_account_currency(account)
|
||||
if account_currency == self.company_currency:
|
||||
voucher_total = order.base_grand_total
|
||||
else:
|
||||
voucher_total = order.grand_total
|
||||
@@ -476,9 +476,18 @@ class JournalEntry(AccountsController):
|
||||
frappe.throw("Accounts table cannot be blank.")
|
||||
|
||||
def set_account_and_party_balance(self):
|
||||
account_balance = {}
|
||||
party_balance = {}
|
||||
for d in self.get("accounts"):
|
||||
d.account_balance = get_balance_on(account=d.account, date=self.posting_date)
|
||||
d.party_balance = get_balance_on(party_type=d.party_type, party=d.party, date=self.posting_date)
|
||||
if d.account not in account_balance:
|
||||
account_balance[d.account] = get_balance_on(account=d.account, date=self.posting_date)
|
||||
|
||||
if (d.party_type, d.party) not in party_balance:
|
||||
party_balance[(d.party_type, d.party)] = get_balance_on(party_type=d.party_type,
|
||||
party=d.party, date=self.posting_date)
|
||||
|
||||
d.account_balance = account_balance[d.account]
|
||||
d.party_balance = party_balance[(d.party_type, d.party)]
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_default_bank_cash_account(company, voucher_type, mode_of_payment=None):
|
||||
@@ -600,8 +609,8 @@ def get_payment_entry_from_sales_order(sales_order):
|
||||
jv = get_payment_entry(so)
|
||||
jv.remark = 'Advance payment received against Sales Order {0}.'.format(so.name)
|
||||
|
||||
party_account = get_party_account(so.company, so.customer, "Customer")
|
||||
party_account_currency = frappe.db.get_value("Account", party_account, "account_currency")
|
||||
party_account = get_party_account("Customer", so.customer, so.company)
|
||||
party_account_currency = get_account_currency(party_account)
|
||||
|
||||
exchange_rate = get_exchange_rate(party_account, party_account_currency, so.company)
|
||||
|
||||
@@ -651,8 +660,8 @@ def get_payment_entry_from_purchase_order(purchase_order):
|
||||
jv = get_payment_entry(po)
|
||||
jv.remark = 'Advance payment made against Purchase Order {0}.'.format(po.name)
|
||||
|
||||
party_account = get_party_account(po.company, po.supplier, "Supplier")
|
||||
party_account_currency = frappe.db.get_value("Account", party_account, "account_currency")
|
||||
party_account = get_party_account("Supplier", po.supplier, po.company)
|
||||
party_account_currency = get_account_currency(party_account)
|
||||
|
||||
exchange_rate = get_exchange_rate(party_account, party_account_currency, po.company)
|
||||
|
||||
@@ -770,7 +779,7 @@ def get_party_account_and_balance(company, party_type, party):
|
||||
frappe.msgprint(_("No Permission"), raise_exception=1)
|
||||
|
||||
from erpnext.accounts.party import get_party_account
|
||||
account = get_party_account(company, party, party_type)
|
||||
account = get_party_account(party_type, party, company)
|
||||
|
||||
account_balance = get_balance_on(account=account)
|
||||
party_balance = get_balance_on(party_type=party_type, party=party)
|
||||
@@ -817,17 +826,20 @@ def get_exchange_rate(account, account_currency, company,
|
||||
if account_currency != company_currency:
|
||||
if reference_type in ("Sales Invoice", "Purchase Invoice") and reference_name:
|
||||
exchange_rate = frappe.db.get_value(reference_type, reference_name, "conversion_rate")
|
||||
elif account_details.account_type == "Bank" and \
|
||||
|
||||
elif account_details and account_details.account_type == "Bank" and \
|
||||
((account_details.root_type == "Asset" and flt(credit) > 0) or
|
||||
(account_details.root_type == "Liability" and debit)):
|
||||
exchange_rate = get_average_exchange_rate(account)
|
||||
|
||||
if not exchange_rate:
|
||||
if not exchange_rate and account_currency:
|
||||
exchange_rate = get_exchange_rate(account_currency, company_currency)
|
||||
|
||||
else:
|
||||
exchange_rate = 1
|
||||
|
||||
return exchange_rate
|
||||
# don't return None or 0 as it is multipled with a value and that value could be lost
|
||||
return exchange_rate or 1
|
||||
|
||||
def get_average_exchange_rate(account):
|
||||
exchange_rate = 0
|
||||
|
||||
@@ -5,6 +5,7 @@ from __future__ import unicode_literals
|
||||
import unittest, frappe
|
||||
from frappe.utils import flt
|
||||
from erpnext.accounts.utils import get_actual_expense, BudgetError, get_fiscal_year
|
||||
from erpnext.exceptions import InvalidAccountCurrency
|
||||
|
||||
|
||||
class TestJournalEntry(unittest.TestCase):
|
||||
@@ -166,15 +167,15 @@ class TestJournalEntry(unittest.TestCase):
|
||||
existing_expense = self.get_actual_expense(posting_date)
|
||||
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
|
||||
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True)
|
||||
|
||||
|
||||
def test_multi_currency(self):
|
||||
jv = make_journal_entry("_Test Bank USD - _TC",
|
||||
"_Test Bank - _TC", 100, exchange_rate=50, save=False)
|
||||
|
||||
|
||||
jv.get("accounts")[1].credit_in_account_currency = 5000
|
||||
jv.submit()
|
||||
|
||||
gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
|
||||
|
||||
gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
|
||||
debit_in_account_currency, credit_in_account_currency
|
||||
from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
|
||||
order by account asc""", jv.name, as_dict=1)
|
||||
@@ -197,12 +198,10 @@ class TestJournalEntry(unittest.TestCase):
|
||||
"credit_in_account_currency": 5000
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for field in ("account_currency", "debit", "debit_in_account_currency", "credit", "credit_in_account_currency"):
|
||||
for i, gle in enumerate(gl_entries):
|
||||
self.assertEquals(expected_values[gle.account][field], gle[field])
|
||||
|
||||
|
||||
|
||||
# cancel
|
||||
jv.cancel()
|
||||
@@ -212,6 +211,40 @@ class TestJournalEntry(unittest.TestCase):
|
||||
|
||||
self.assertFalse(gle)
|
||||
|
||||
def test_disallow_change_in_account_currency_for_a_party(self):
|
||||
# create jv in USD
|
||||
jv = make_journal_entry("_Test Bank USD - _TC",
|
||||
"_Test Receivable USD - _TC", 100, save=False)
|
||||
|
||||
jv.accounts[1].update({
|
||||
"party_type": "Customer",
|
||||
"party": "_Test Customer USD"
|
||||
})
|
||||
|
||||
jv.submit()
|
||||
|
||||
# create jv in USD, but account currency in INR
|
||||
jv = make_journal_entry("_Test Bank - _TC",
|
||||
"_Test Receivable - _TC", 100, save=False)
|
||||
|
||||
jv.accounts[1].update({
|
||||
"party_type": "Customer",
|
||||
"party": "_Test Customer USD"
|
||||
})
|
||||
|
||||
self.assertRaises(InvalidAccountCurrency, jv.submit)
|
||||
|
||||
# back in USD
|
||||
jv = make_journal_entry("_Test Bank USD - _TC",
|
||||
"_Test Receivable USD - _TC", 100, save=False)
|
||||
|
||||
jv.accounts[1].update({
|
||||
"party_type": "Customer",
|
||||
"party": "_Test Customer USD"
|
||||
})
|
||||
|
||||
jv.submit()
|
||||
|
||||
def make_journal_entry(account1, account2, amount, cost_center=None, exchange_rate=1, save=True, submit=False):
|
||||
jv = frappe.new_doc("Journal Entry")
|
||||
jv.posting_date = "2013-02-14"
|
||||
@@ -231,7 +264,7 @@ def make_journal_entry(account1, account2, amount, cost_center=None, exchange_ra
|
||||
"cost_center": cost_center,
|
||||
"credit_in_account_currency": amount if amount > 0 else 0,
|
||||
"debit_in_account_currency": abs(amount) if amount < 0 else 0,
|
||||
exchange_rate: exchange_rate
|
||||
"exchange_rate": exchange_rate
|
||||
}
|
||||
])
|
||||
if save or submit:
|
||||
|
||||
@@ -44,7 +44,7 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
refresh: function() {
|
||||
this.frm.disable_save();
|
||||
},
|
||||
@@ -77,8 +77,8 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
||||
var invoices = [];
|
||||
|
||||
$.each(me.frm.doc.invoices || [], function(i, row) {
|
||||
if (row.invoice_number && !inList(invoices, row.invoice_number))
|
||||
invoices.push(row.invoice_number);
|
||||
if (row.invoice_number && !inList(invoices, row.invoice_number))
|
||||
invoices.push(row.invoice_type + " | " + row.invoice_number);
|
||||
});
|
||||
|
||||
frappe.meta.get_docfield("Payment Reconciliation Payment", "invoice_number",
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Column Break",
|
||||
"label": "",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
@@ -362,7 +362,7 @@
|
||||
"is_submittable": 0,
|
||||
"issingle": 1,
|
||||
"istable": 0,
|
||||
"modified": "2015-02-05 05:11:42.105088",
|
||||
"modified": "2015-09-21 03:41:24.672227",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Reconciliation",
|
||||
|
||||
@@ -79,8 +79,12 @@ class PaymentReconciliation(Document):
|
||||
`tabGL Entry`
|
||||
where
|
||||
party_type = %(party_type)s and party = %(party)s
|
||||
and voucher_type != "Journal Entry"
|
||||
and account = %(account)s and {dr_or_cr} > 0 {cond}
|
||||
and (CASE
|
||||
WHEN voucher_type = 'Journal Entry'
|
||||
THEN ifnull(against_voucher, '') = ''
|
||||
ELSE 1=1
|
||||
END)
|
||||
group by voucher_type, voucher_no
|
||||
""".format(**{
|
||||
"cond": cond,
|
||||
@@ -136,12 +140,16 @@ class PaymentReconciliation(Document):
|
||||
ent.outstanding_amount = e.get('outstanding_amount')
|
||||
|
||||
def reconcile(self, args):
|
||||
for e in self.get('payments'):
|
||||
if e.invoice_number and " | " in e.invoice_number:
|
||||
e.invoice_type, e.invoice_number = e.invoice_number.split(" | ")
|
||||
|
||||
self.get_invoice_entries()
|
||||
self.validate_invoice()
|
||||
dr_or_cr = "credit" if self.party_type == "Customer" else "debit"
|
||||
lst = []
|
||||
for e in self.get('payments'):
|
||||
if e.invoice_type and e.invoice_number and e.allocated_amount:
|
||||
if e.invoice_number and e.allocated_amount:
|
||||
lst.append({
|
||||
'voucher_no' : e.journal_entry,
|
||||
'voucher_detail_no' : e.voucher_detail_number,
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Column Break",
|
||||
"label": "",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
@@ -135,51 +135,6 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "allocated_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Allocated amount",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "Sales Invoice",
|
||||
"fieldname": "invoice_type",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Invoice Type",
|
||||
"no_copy": 0,
|
||||
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
@@ -202,6 +157,28 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "allocated_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Allocated amount",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
@@ -243,27 +220,6 @@
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "col_break2",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Column Break",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
@@ -273,7 +229,7 @@
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2014-12-25 16:26:48.345281",
|
||||
"modified": "2015-09-21 03:39:40.320070",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Reconciliation Payment",
|
||||
|
||||
@@ -7,6 +7,7 @@ from frappe import _, scrub
|
||||
from frappe.utils import flt
|
||||
from frappe.model.document import Document
|
||||
import json
|
||||
from erpnext.accounts.utils import get_account_currency
|
||||
|
||||
class PaymentTool(Document):
|
||||
def make_journal_entry(self):
|
||||
@@ -59,7 +60,7 @@ def get_outstanding_vouchers(args):
|
||||
|
||||
args = json.loads(args)
|
||||
|
||||
party_account_currency = frappe.db.get_value("Account", args.get("party_account"), "account_currency")
|
||||
party_account_currency = get_account_currency(args.get("party_account"))
|
||||
company_currency = frappe.db.get_value("Company", args.get("company"), "default_currency")
|
||||
|
||||
if args.get("party_type") == "Customer" and args.get("received_or_paid") == "Received":
|
||||
@@ -71,7 +72,7 @@ def get_outstanding_vouchers(args):
|
||||
|
||||
# Get all outstanding sales /purchase invoices
|
||||
outstanding_invoices = get_outstanding_invoices(amount_query, args.get("party_account"),
|
||||
args.get("party_type"), args.get("party"), with_journal_entry=False)
|
||||
args.get("party_type"), args.get("party"))
|
||||
|
||||
# Get all SO / PO which are not fully billed or aginst which full advance not paid
|
||||
orders_to_be_billed = get_orders_to_be_billed(args.get("party_type"), args.get("party"),
|
||||
@@ -112,7 +113,7 @@ def get_orders_to_be_billed(party_type, party, party_account_currency, company_c
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_against_voucher_amount(against_voucher_type, against_voucher_no, party_account, company):
|
||||
party_account_currency = frappe.db.get_value("Account", party_account, "account_currency")
|
||||
party_account_currency = get_account_currency(party_account)
|
||||
company_currency = frappe.db.get_value("Company", company, "default_currency")
|
||||
ref_field = "base_grand_total" if party_account_currency == company_currency else "grand_total"
|
||||
|
||||
|
||||
@@ -2375,4 +2375,4 @@
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "title"
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import frappe.defaults
|
||||
|
||||
from erpnext.controllers.buying_controller import BuyingController
|
||||
from erpnext.accounts.party import get_party_account, get_due_date
|
||||
from erpnext.accounts.utils import get_account_currency
|
||||
|
||||
form_grid_templates = {
|
||||
"items": "templates/form_grid/item_grid.html"
|
||||
@@ -66,7 +67,7 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
def set_missing_values(self, for_validate=False):
|
||||
if not self.credit_to:
|
||||
self.credit_to = get_party_account(self.company, self.supplier, "Supplier")
|
||||
self.credit_to = get_party_account("Supplier", self.supplier, self.company)
|
||||
if not self.due_date:
|
||||
self.due_date = get_due_date(self.posting_date, "Supplier", self.supplier, self.company)
|
||||
|
||||
@@ -91,7 +92,7 @@ class PurchaseInvoice(BuyingController):
|
||||
throw(_("Conversion rate cannot be 0 or 1"))
|
||||
|
||||
def validate_credit_to_acc(self):
|
||||
account = frappe.db.get_value("Account", self.credit_to,
|
||||
account = frappe.db.get_value("Account", self.credit_to,
|
||||
["account_type", "report_type", "account_currency"], as_dict=True)
|
||||
|
||||
if account.report_type != "Balance Sheet":
|
||||
@@ -99,7 +100,7 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
if self.supplier and account.account_type != "Payable":
|
||||
frappe.throw(_("Credit To account must be a Payable account"))
|
||||
|
||||
|
||||
self.party_account_currency = account.account_currency
|
||||
|
||||
def check_for_stopped_status(self):
|
||||
@@ -251,7 +252,7 @@ class PurchaseInvoice(BuyingController):
|
||||
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
|
||||
|
||||
gl_entries = []
|
||||
|
||||
|
||||
# parent's gl entry
|
||||
if self.base_grand_total:
|
||||
gl_entries.append(
|
||||
@@ -272,10 +273,10 @@ class PurchaseInvoice(BuyingController):
|
||||
valuation_tax = {}
|
||||
for tax in self.get("taxes"):
|
||||
if tax.category in ("Total", "Valuation and Total") and flt(tax.base_tax_amount_after_discount_amount):
|
||||
account_currency = frappe.db.get_value("Account", tax.account_head, "account_currency")
|
||||
|
||||
account_currency = get_account_currency(tax.account_head)
|
||||
|
||||
dr_or_cr = "debit" if tax.add_deduct_tax == "Add" else "credit"
|
||||
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": tax.account_head,
|
||||
@@ -301,7 +302,7 @@ class PurchaseInvoice(BuyingController):
|
||||
stock_items = self.get_stock_items()
|
||||
for item in self.get("items"):
|
||||
if flt(item.base_net_amount):
|
||||
account_currency = frappe.db.get_value("Account", item.expense_account, "account_currency")
|
||||
account_currency = get_account_currency(item.expense_account)
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": item.expense_account,
|
||||
@@ -363,8 +364,8 @@ class PurchaseInvoice(BuyingController):
|
||||
# writeoff account includes petty difference in the invoice amount
|
||||
# and the amount that is paid
|
||||
if self.write_off_account and flt(self.write_off_amount):
|
||||
write_off_account_currency = frappe.db.get_value("Account", self.write_off_account, "account_currency")
|
||||
|
||||
write_off_account_currency = get_account_currency(self.write_off_account)
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": self.credit_to,
|
||||
|
||||
@@ -10,7 +10,7 @@ from frappe.utils import cint
|
||||
import frappe.defaults
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \
|
||||
test_records as pr_test_records
|
||||
from erpnext.controllers.accounts_controller import InvalidCurrency
|
||||
from erpnext.exceptions import InvalidCurrency
|
||||
|
||||
test_dependencies = ["Item", "Cost Center"]
|
||||
test_ignore = ["Serial No"]
|
||||
@@ -219,7 +219,7 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
pi.load_from_db()
|
||||
|
||||
self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
|
||||
where reference_type='Purchase Invoice'
|
||||
where reference_type='Purchase Invoice'
|
||||
and reference_name=%s and debit_in_account_currency=300""", pi.name))
|
||||
|
||||
self.assertEqual(pi.outstanding_amount, 1212.30)
|
||||
@@ -237,17 +237,17 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
existing_purchase_cost = frappe.db.sql("""select sum(ifnull(base_net_amount, 0))
|
||||
from `tabPurchase Invoice Item` where project_name = '_Test Project' and docstatus=1""")
|
||||
existing_purchase_cost = existing_purchase_cost and existing_purchase_cost[0][0] or 0
|
||||
|
||||
|
||||
pi = make_purchase_invoice(currency="USD", conversion_rate=60, project_name="_Test Project")
|
||||
self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"),
|
||||
self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"),
|
||||
existing_purchase_cost + 15000)
|
||||
|
||||
pi1 = make_purchase_invoice(qty=10, project_name="_Test Project")
|
||||
self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"),
|
||||
self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"),
|
||||
existing_purchase_cost + 15500)
|
||||
|
||||
pi1.cancel()
|
||||
self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"),
|
||||
self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"),
|
||||
existing_purchase_cost + 15000)
|
||||
|
||||
pi.cancel()
|
||||
@@ -278,14 +278,14 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
self.assertEquals(expected_values[gle.account][1], gle.credit)
|
||||
|
||||
set_perpetual_inventory(0)
|
||||
|
||||
|
||||
def test_multi_currency_gle(self):
|
||||
set_perpetual_inventory(0)
|
||||
|
||||
pi = make_purchase_invoice(supplier="_Test Supplier USD", credit_to="_Test Payable USD - _TC",
|
||||
|
||||
pi = make_purchase_invoice(supplier="_Test Supplier USD", credit_to="_Test Payable USD - _TC",
|
||||
currency="USD", conversion_rate=50)
|
||||
|
||||
gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
|
||||
gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
|
||||
debit_in_account_currency, credit_in_account_currency
|
||||
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
|
||||
order by account asc""", pi.name, as_dict=1)
|
||||
@@ -308,16 +308,16 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
"credit_in_account_currency": 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for field in ("account_currency", "debit", "debit_in_account_currency", "credit", "credit_in_account_currency"):
|
||||
for i, gle in enumerate(gl_entries):
|
||||
self.assertEquals(expected_values[gle.account][field], gle[field])
|
||||
|
||||
|
||||
|
||||
|
||||
# Check for valid currency
|
||||
pi1 = make_purchase_invoice(supplier="_Test Supplier USD", credit_to="_Test Payable USD - _TC",
|
||||
do_not_save=True)
|
||||
|
||||
|
||||
self.assertRaises(InvalidCurrency, pi1.save)
|
||||
|
||||
# cancel
|
||||
|
||||
@@ -504,7 +504,7 @@
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-08-19 12:46:32.687299",
|
||||
"modified": "2015-08-28 02:57:08.769473",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Taxes and Charges",
|
||||
|
||||
@@ -36,6 +36,11 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
refresh: function(doc, dt, dn) {
|
||||
this._super();
|
||||
|
||||
if(cur_frm.msgbox && cur_frm.msgbox.$wrapper.is(":visible")) {
|
||||
// hide new msgbox
|
||||
cur_frm.msgbox.hide();
|
||||
}
|
||||
|
||||
cur_frm.dashboard.reset();
|
||||
|
||||
this.frm.toggle_reqd("due_date", !this.frm.doc.is_return);
|
||||
@@ -146,7 +151,9 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
method: "set_missing_values",
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
cur_frm.pos_print_format = r.message.print_format;
|
||||
if(r.message && r.message.print_format) {
|
||||
cur_frm.pos_print_format = r.message.print_format;
|
||||
}
|
||||
cur_frm.doc.__missing_values_set = true;
|
||||
me.frm.script_manager.trigger("update_stock");
|
||||
frappe.model.set_default_values(me.frm.doc);
|
||||
@@ -175,7 +182,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
me.apply_pricing_rule();
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
debit_to: function() {
|
||||
var me = this;
|
||||
if(this.frm.doc.debit_to) {
|
||||
@@ -193,7 +200,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
allocated_amount: function() {
|
||||
@@ -417,9 +424,9 @@ cur_frm.cscript.on_submit = function(doc, cdt, cdn) {
|
||||
})
|
||||
|
||||
if(cur_frm.doc.is_pos) {
|
||||
frappe.msgprint('<a class="btn btn-primary" \
|
||||
cur_frm.msgbox = frappe.msgprint('<a class="btn btn-primary" \
|
||||
onclick="cur_frm.print_preview.printit(true)" style="margin-right: 5px;">Print</a>\
|
||||
<a class="btn btn-default" href="#Form/Sales Invoice/New">New</a>');
|
||||
<a class="btn btn-default" href="#Form/Sales Invoice/New Sales Invoice">New</a>');
|
||||
|
||||
} else if(cint(frappe.boot.notification_settings.sales_invoice)) {
|
||||
cur_frm.email_doc(frappe.boot.notification_settings.sales_invoice_message);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,7 @@ from erpnext.controllers.stock_controller import update_gl_entries_after
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
|
||||
from erpnext.controllers.selling_controller import SellingController
|
||||
from erpnext.accounts.utils import get_account_currency
|
||||
|
||||
form_grid_templates = {
|
||||
"items": "templates/form_grid/item_grid.html"
|
||||
@@ -35,6 +36,15 @@ class SalesInvoice(SellingController):
|
||||
'overflow_type': 'billing'
|
||||
}]
|
||||
|
||||
def set_indicator(self):
|
||||
"""Set indicator for portal"""
|
||||
if self.outstanding_amount > 0:
|
||||
self.indicator_color = "orange"
|
||||
self.indicator_title = _("Unpaid")
|
||||
else:
|
||||
self.indicator_color = "green"
|
||||
self.indicator_title = _("Paid")
|
||||
|
||||
def validate(self):
|
||||
super(SalesInvoice, self).validate()
|
||||
self.validate_posting_time()
|
||||
@@ -90,7 +100,7 @@ class SalesInvoice(SellingController):
|
||||
|
||||
# this sequence because outstanding may get -ve
|
||||
self.make_gl_entries()
|
||||
|
||||
|
||||
if not self.is_return:
|
||||
self.update_billing_status_for_zero_amount_refdoc("Sales Order")
|
||||
self.check_credit_limit()
|
||||
@@ -161,10 +171,10 @@ class SalesInvoice(SellingController):
|
||||
'extra_cond': """ and exists (select name from `tabSales Invoice` where name=`tabSales Invoice Item`.parent and update_stock=1 and is_return=1)"""
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
def check_credit_limit(self):
|
||||
from erpnext.selling.doctype.customer.customer import check_credit_limit
|
||||
|
||||
|
||||
validate_against_credit_limit = False
|
||||
for d in self.get("items"):
|
||||
if not (d.sales_order or d.delivery_note):
|
||||
@@ -177,7 +187,7 @@ class SalesInvoice(SellingController):
|
||||
pos = self.set_pos_fields(for_validate)
|
||||
|
||||
if not self.debit_to:
|
||||
self.debit_to = get_party_account(self.company, self.customer, "Customer")
|
||||
self.debit_to = get_party_account("Customer", self.customer, self.company)
|
||||
if not self.due_date and self.customer:
|
||||
self.due_date = get_due_date(self.posting_date, "Customer", self.customer, self.company)
|
||||
|
||||
@@ -282,7 +292,7 @@ class SalesInvoice(SellingController):
|
||||
reconcile_against_document(lst)
|
||||
|
||||
def validate_debit_to_acc(self):
|
||||
account = frappe.db.get_value("Account", self.debit_to,
|
||||
account = frappe.db.get_value("Account", self.debit_to,
|
||||
["account_type", "report_type", "account_currency"], as_dict=True)
|
||||
|
||||
if account.report_type != "Balance Sheet":
|
||||
@@ -290,7 +300,7 @@ class SalesInvoice(SellingController):
|
||||
|
||||
if self.customer and account.account_type != "Receivable":
|
||||
frappe.throw(_("Debit To account must be a Receivable account"))
|
||||
|
||||
|
||||
self.party_account_currency = account.account_currency
|
||||
|
||||
def validate_fixed_asset_account(self):
|
||||
@@ -437,18 +447,18 @@ class SalesInvoice(SellingController):
|
||||
if cint(self.is_pos) == 1:
|
||||
if flt(self.paid_amount) == 0:
|
||||
if self.cash_bank_account:
|
||||
frappe.db.set(self, 'paid_amount',
|
||||
flt(flt(self.grand_total) - flt(self.write_off_amount), self.precision("paid_amount")))
|
||||
frappe.db.set(self, 'paid_amount',
|
||||
flt(flt(self.grand_total) - flt(self.write_off_amount), self.precision("paid_amount")))
|
||||
else:
|
||||
# show message that the amount is not paid
|
||||
frappe.db.set(self,'paid_amount',0)
|
||||
frappe.msgprint(_("Note: Payment Entry will not be created since 'Cash or Bank Account' was not specified"))
|
||||
else:
|
||||
frappe.db.set(self,'paid_amount',0)
|
||||
|
||||
frappe.db.set(self, 'base_paid_amount',
|
||||
|
||||
frappe.db.set(self, 'base_paid_amount',
|
||||
flt(self.paid_amount*self.conversion_rate, self.precision("base_paid_amount")))
|
||||
|
||||
|
||||
def check_prev_docstatus(self):
|
||||
for d in self.get('items'):
|
||||
if d.sales_order and frappe.db.get_value("Sales Order", d.sales_order, "docstatus") != 1:
|
||||
@@ -487,7 +497,7 @@ class SalesInvoice(SellingController):
|
||||
from erpnext.accounts.general_ledger import merge_similar_entries
|
||||
|
||||
gl_entries = []
|
||||
|
||||
|
||||
self.make_customer_gl_entry(gl_entries)
|
||||
|
||||
self.make_tax_gl_entries(gl_entries)
|
||||
@@ -522,7 +532,7 @@ class SalesInvoice(SellingController):
|
||||
def make_tax_gl_entries(self, gl_entries):
|
||||
for tax in self.get("taxes"):
|
||||
if flt(tax.base_tax_amount_after_discount_amount):
|
||||
account_currency = frappe.db.get_value("Account", tax.account_head, "account_currency")
|
||||
account_currency = get_account_currency(tax.account_head)
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": tax.account_head,
|
||||
@@ -538,7 +548,7 @@ class SalesInvoice(SellingController):
|
||||
# income account gl entries
|
||||
for item in self.get("items"):
|
||||
if flt(item.base_net_amount):
|
||||
account_currency = frappe.db.get_value("Account", item.income_account, "account_currency")
|
||||
account_currency = get_account_currency(item.income_account)
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": item.income_account,
|
||||
@@ -557,7 +567,7 @@ class SalesInvoice(SellingController):
|
||||
|
||||
def make_pos_gl_entries(self, gl_entries):
|
||||
if cint(self.is_pos) and self.cash_bank_account and self.paid_amount:
|
||||
bank_account_currency = frappe.db.get_value("Account", self.cash_bank_account, "account_currency")
|
||||
bank_account_currency = get_account_currency(self.cash_bank_account)
|
||||
# POS, make payment entries
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
@@ -585,8 +595,8 @@ class SalesInvoice(SellingController):
|
||||
def make_write_off_gl_entry(self, gl_entries):
|
||||
# write off entries, applicable if only pos
|
||||
if self.write_off_account and self.write_off_amount:
|
||||
write_off_account_currency = frappe.db.get_value("Account", self.write_off_account, "account_currency")
|
||||
|
||||
write_off_account_currency = get_account_currency(self.write_off_account)
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": self.debit_to,
|
||||
|
||||
@@ -7,8 +7,7 @@ import unittest, copy
|
||||
from frappe.utils import nowdate, add_days, flt
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
|
||||
from erpnext.controllers.accounts_controller import InvalidCurrency
|
||||
from erpnext.accounts.doctype.gl_entry.gl_entry import InvalidAccountCurrency
|
||||
from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency
|
||||
|
||||
class TestSalesInvoice(unittest.TestCase):
|
||||
def make(self):
|
||||
@@ -842,13 +841,13 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
self.assertEquals(si.total_taxes_and_charges, 234.44)
|
||||
self.assertEquals(si.base_grand_total, 859.44)
|
||||
self.assertEquals(si.grand_total, 859.44)
|
||||
|
||||
|
||||
def test_multi_currency_gle(self):
|
||||
set_perpetual_inventory(0)
|
||||
si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
|
||||
si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
|
||||
currency="USD", conversion_rate=50)
|
||||
|
||||
gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
|
||||
gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
|
||||
debit_in_account_currency, credit_in_account_currency
|
||||
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
|
||||
order by account asc""", si.name, as_dict=1)
|
||||
@@ -871,7 +870,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
"credit_in_account_currency": 5000
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for field in ("account_currency", "debit", "debit_in_account_currency", "credit", "credit_in_account_currency"):
|
||||
for i, gle in enumerate(gl_entries):
|
||||
self.assertEquals(expected_values[gle.account][field], gle[field])
|
||||
@@ -883,38 +882,38 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
|
||||
|
||||
self.assertFalse(gle)
|
||||
|
||||
|
||||
def test_invalid_currency(self):
|
||||
# Customer currency = USD
|
||||
|
||||
|
||||
# Transaction currency cannot be INR
|
||||
si1 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
|
||||
si1 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
|
||||
do_not_save=True)
|
||||
|
||||
|
||||
self.assertRaises(InvalidCurrency, si1.save)
|
||||
|
||||
|
||||
# Transaction currency cannot be EUR
|
||||
si2 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
|
||||
si2 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
|
||||
currency="EUR", conversion_rate=80, do_not_save=True)
|
||||
|
||||
|
||||
self.assertRaises(InvalidCurrency, si2.save)
|
||||
|
||||
|
||||
# Transaction currency only allowed in USD
|
||||
si3 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
|
||||
si3 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
|
||||
currency="USD", conversion_rate=50)
|
||||
|
||||
|
||||
# Party Account currency must be in USD, as there is existing GLE with USD
|
||||
si4 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable - _TC",
|
||||
si4 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable - _TC",
|
||||
currency="USD", conversion_rate=50, do_not_submit=True)
|
||||
|
||||
|
||||
self.assertRaises(InvalidAccountCurrency, si4.submit)
|
||||
|
||||
|
||||
# Party Account currency must be in USD, force customer currency as there is no GLE
|
||||
|
||||
|
||||
si3.cancel()
|
||||
si5 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable - _TC",
|
||||
si5 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable - _TC",
|
||||
currency="USD", conversion_rate=50, do_not_submit=True)
|
||||
|
||||
|
||||
self.assertRaises(InvalidAccountCurrency, si5.submit)
|
||||
|
||||
def create_sales_invoice(**args):
|
||||
|
||||
@@ -456,7 +456,7 @@
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-08-19 12:46:33.165519",
|
||||
"modified": "2015-08-28 02:57:00.766305",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Taxes and Charges",
|
||||
|
||||
@@ -5,6 +5,3 @@ cur_frm.cscript.tax_table = "Sales Taxes and Charges";
|
||||
|
||||
{% include "public/js/controllers/accounts.js" %}
|
||||
|
||||
frappe.ui.form.on("Sales Taxes and Charges Template", "onload", function(frm) {
|
||||
erpnext.add_applicable_territory();
|
||||
});
|
||||
|
||||
@@ -164,29 +164,6 @@
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "Specify a list of Territories, for which, this Taxes Master is valid",
|
||||
"fieldname": "territories",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Valid for Territories",
|
||||
"no_copy": 0,
|
||||
"options": "Applicable Territory",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
@@ -198,7 +175,7 @@
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-09-11 12:19:46.488710",
|
||||
"modified": "2015-09-17 07:09:28.797959",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Taxes and Charges Template",
|
||||
@@ -206,7 +183,7 @@
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
|
||||
@@ -5,7 +5,6 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from erpnext.controllers.accounts_controller import validate_taxes_and_charges, validate_inclusive_tax
|
||||
from frappe.utils.nestedset import get_root_of
|
||||
|
||||
class SalesTaxesandChargesTemplate(Document):
|
||||
def validate(self):
|
||||
@@ -20,10 +19,6 @@ def valdiate_taxes_and_charges_template(doc):
|
||||
where ifnull(is_default,0) = 1 and name != %s and company = %s""".format(doc.doctype),
|
||||
(doc.name, doc.company))
|
||||
|
||||
if doc.meta.get_field("territories"):
|
||||
if not doc.territories:
|
||||
doc.append("territories", {"territory": get_root_of("Territory") })
|
||||
|
||||
for tax in doc.get("taxes"):
|
||||
validate_taxes_and_charges(tax)
|
||||
validate_inclusive_tax(tax, doc)
|
||||
|
||||
@@ -20,19 +20,7 @@
|
||||
"rate": 6.36
|
||||
}
|
||||
],
|
||||
"title": "_Test Sales Taxes and Charges Template",
|
||||
"territories": [
|
||||
{
|
||||
"doctype": "Applicable Territory",
|
||||
"parentfield": "territories",
|
||||
"territory": "All Territories"
|
||||
},
|
||||
{
|
||||
"doctype": "Applicable Territory",
|
||||
"parentfield": "territories",
|
||||
"territory": "_Test Territory Rest Of The World"
|
||||
}
|
||||
]
|
||||
"title": "_Test Sales Taxes and Charges Template"
|
||||
},
|
||||
{
|
||||
"company": "_Test Company",
|
||||
@@ -115,14 +103,7 @@
|
||||
"row_id": 7
|
||||
}
|
||||
],
|
||||
"title": "_Test India Tax Master",
|
||||
"territories": [
|
||||
{
|
||||
"doctype": "Applicable Territory",
|
||||
"parentfield": "territories",
|
||||
"territory": "_Test Territory India"
|
||||
}
|
||||
]
|
||||
"title": "_Test India Tax Master"
|
||||
},
|
||||
{
|
||||
"company": "_Test Company",
|
||||
@@ -145,13 +126,76 @@
|
||||
"rate": 4
|
||||
}
|
||||
],
|
||||
"title": "_Test Sales Taxes and Charges Template - Rest of the World",
|
||||
"territories": [
|
||||
"title": "_Test Sales Taxes and Charges Template - Rest of the World"
|
||||
},
|
||||
{
|
||||
"company": "_Test Company",
|
||||
"doctype": "Sales Taxes and Charges Template",
|
||||
"taxes": [
|
||||
{
|
||||
"doctype": "Applicable Territory",
|
||||
"parentfield": "territories",
|
||||
"territory": "_Test Territory Rest Of The World"
|
||||
"account_head": "_Test Account VAT - _TC",
|
||||
"charge_type": "On Net Total",
|
||||
"description": "VAT",
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "taxes",
|
||||
"rate": 12
|
||||
},
|
||||
{
|
||||
"account_head": "_Test Account Service Tax - _TC",
|
||||
"charge_type": "On Net Total",
|
||||
"description": "Service Tax",
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "taxes",
|
||||
"rate": 4
|
||||
}
|
||||
]
|
||||
],
|
||||
"title": "_Test Sales Taxes and Charges Template 1"
|
||||
},
|
||||
{
|
||||
"company": "_Test Company",
|
||||
"doctype": "Sales Taxes and Charges Template",
|
||||
"taxes": [
|
||||
{
|
||||
"account_head": "_Test Account VAT - _TC",
|
||||
"charge_type": "On Net Total",
|
||||
"description": "VAT",
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "taxes",
|
||||
"rate": 12
|
||||
},
|
||||
{
|
||||
"account_head": "_Test Account Service Tax - _TC",
|
||||
"charge_type": "On Net Total",
|
||||
"description": "Service Tax",
|
||||
"doctype": "Sales Taxes and Charges",
|
||||
"parentfield": "taxes",
|
||||
"rate": 4
|
||||
}
|
||||
],
|
||||
"title": "_Test Sales Taxes and Charges Template 2"
|
||||
},
|
||||
{
|
||||
"doctype" : "Sales Taxes and Charges Template",
|
||||
"title": "_Test Tax 1",
|
||||
"company": "_Test Company",
|
||||
"taxes":[{
|
||||
"charge_type": "Actual",
|
||||
"account_head": "Sales Expenses - _TC",
|
||||
"cost_center": "Main - _TC",
|
||||
"description": "Test Shopping cart taxes with Tax Rule",
|
||||
"tax_amount": 1000
|
||||
}]
|
||||
},
|
||||
{
|
||||
"doctype" : "Sales Taxes and Charges Template",
|
||||
"title": "_Test Tax 2",
|
||||
"company": "_Test Company",
|
||||
"taxes":[{
|
||||
"charge_type": "Actual",
|
||||
"account_head": "Sales Expenses - _TC",
|
||||
"cost_center": "Main - _TC",
|
||||
"description": "Test Shopping cart taxes with Tax Rule",
|
||||
"tax_amount": 200
|
||||
}]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
$.extend(cur_frm.cscript, {
|
||||
onload: function() {
|
||||
erpnext.add_applicable_territory();
|
||||
}
|
||||
});
|
||||
@@ -1,388 +1,397 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "field:label",
|
||||
"creation": "2013-06-25 11:48:03",
|
||||
"custom": 0,
|
||||
"description": "Specify conditions to calculate shipping amount",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "field:label",
|
||||
"creation": "2013-06-25 11:48:03",
|
||||
"custom": 0,
|
||||
"description": "Specify conditions to calculate shipping amount",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "example: Next Day Shipping",
|
||||
"fieldname": "label",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Shipping Rule Label",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "example: Next Day Shipping",
|
||||
"fieldname": "label",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Shipping Rule Label",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_2",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "disabled",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Disabled",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "Net Total",
|
||||
"fieldname": "calculate_based_on",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Calculate Based On",
|
||||
"no_copy": 0,
|
||||
"options": "Net Total\nNet Weight",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "Net Total",
|
||||
"fieldname": "calculate_based_on",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Calculate Based On",
|
||||
"no_copy": 0,
|
||||
"options": "Net Total\nNet Weight",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "rule_conditions_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Shipping Rule Conditions",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "eval:!doc.disabled",
|
||||
"fieldname": "rule_conditions_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Shipping Rule Conditions",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "conditions",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Shipping Rule Conditions",
|
||||
"no_copy": 0,
|
||||
"options": "Shipping Rule Condition",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "conditions",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Shipping Rule Conditions",
|
||||
"no_copy": 0,
|
||||
"options": "Shipping Rule Condition",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "section_break_6",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "eval:!doc.disabled",
|
||||
"fieldname": "section_break_6",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Valid for Countries",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "Specify a list of Territories, for which, this Shipping Rule is valid",
|
||||
"fieldname": "territories",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Valid For Territories",
|
||||
"no_copy": 0,
|
||||
"options": "Applicable Territory",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "worldwide_shipping",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Worldwide Shipping",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_8",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "eval:!doc.worldwide_shipping",
|
||||
"fieldname": "countries",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Valid for Countries",
|
||||
"no_copy": 0,
|
||||
"options": "Shipping Rule Country",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Company",
|
||||
"no_copy": 0,
|
||||
"options": "Company",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "eval:!doc.disabled",
|
||||
"fieldname": "section_break_10",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "section_break_10",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Company",
|
||||
"no_copy": 0,
|
||||
"options": "Company",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "account",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Shipping Account",
|
||||
"no_copy": 0,
|
||||
"options": "Account",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_12",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_12",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "account",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Shipping Account",
|
||||
"no_copy": 0,
|
||||
"options": "Account",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "cost_center",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Cost Center",
|
||||
"no_copy": 0,
|
||||
"options": "Cost Center",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "cost_center",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Cost Center",
|
||||
"no_copy": 0,
|
||||
"options": "Cost Center",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "icon-truck",
|
||||
"idx": 1,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-09-07 15:51:26",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Shipping Rule",
|
||||
"owner": "Administrator",
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "icon-truck",
|
||||
"idx": 1,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-09-22 08:30:57.226342",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Shipping Rule",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 0,
|
||||
"submit": 0,
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 0,
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Sales User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 0,
|
||||
"submit": 0,
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Sales User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 0,
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Sales Master Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Sales Master Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"read_only": 0,
|
||||
],
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,12 @@ class ShippingRule(Document):
|
||||
self.sort_shipping_rule_conditions()
|
||||
self.validate_overlapping_shipping_rule_conditions()
|
||||
|
||||
if self.worldwide_shipping:
|
||||
self.countries = []
|
||||
|
||||
elif not len([d.country for d in self.countries if d.country]):
|
||||
frappe.throw(_("Please specify a country for this Shipping Rule or check Worldwide Shipping"))
|
||||
|
||||
def validate_from_to_values(self):
|
||||
zero_to_values = []
|
||||
|
||||
|
||||
@@ -1,116 +1,100 @@
|
||||
[
|
||||
{
|
||||
"account": "_Test Account Shipping Charges - _TC",
|
||||
"calculate_based_on": "Net Total",
|
||||
"company": "_Test Company",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"doctype": "Shipping Rule",
|
||||
"label": "_Test Shipping Rule",
|
||||
"name": "_Test Shipping Rule",
|
||||
"account": "_Test Account Shipping Charges - _TC",
|
||||
"calculate_based_on": "Net Total",
|
||||
"company": "_Test Company",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"doctype": "Shipping Rule",
|
||||
"label": "_Test Shipping Rule",
|
||||
"name": "_Test Shipping Rule",
|
||||
"conditions": [
|
||||
{
|
||||
"doctype": "Shipping Rule Condition",
|
||||
"from_value": 0,
|
||||
"parentfield": "conditions",
|
||||
"shipping_amount": 50.0,
|
||||
"doctype": "Shipping Rule Condition",
|
||||
"from_value": 0,
|
||||
"parentfield": "conditions",
|
||||
"shipping_amount": 50.0,
|
||||
"to_value": 100
|
||||
},
|
||||
},
|
||||
{
|
||||
"doctype": "Shipping Rule Condition",
|
||||
"from_value": 101,
|
||||
"parentfield": "conditions",
|
||||
"shipping_amount": 100.0,
|
||||
"doctype": "Shipping Rule Condition",
|
||||
"from_value": 101,
|
||||
"parentfield": "conditions",
|
||||
"shipping_amount": 100.0,
|
||||
"to_value": 200
|
||||
},
|
||||
},
|
||||
{
|
||||
"doctype": "Shipping Rule Condition",
|
||||
"from_value": 201,
|
||||
"parentfield": "conditions",
|
||||
"doctype": "Shipping Rule Condition",
|
||||
"from_value": 201,
|
||||
"parentfield": "conditions",
|
||||
"shipping_amount": 0.0
|
||||
}
|
||||
],
|
||||
"territories": [
|
||||
{
|
||||
"doctype": "Applicable Territory",
|
||||
"parentfield": "territories",
|
||||
"territory": "_Test Territory"
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
"worldwide_shipping": 1
|
||||
},
|
||||
{
|
||||
"account": "_Test Account Shipping Charges - _TC",
|
||||
"calculate_based_on": "Net Total",
|
||||
"company": "_Test Company",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"doctype": "Shipping Rule",
|
||||
"label": "_Test Shipping Rule - India",
|
||||
"name": "_Test Shipping Rule - India",
|
||||
"account": "_Test Account Shipping Charges - _TC",
|
||||
"calculate_based_on": "Net Total",
|
||||
"company": "_Test Company",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"doctype": "Shipping Rule",
|
||||
"label": "_Test Shipping Rule - India",
|
||||
"name": "_Test Shipping Rule - India",
|
||||
"conditions": [
|
||||
{
|
||||
"doctype": "Shipping Rule Condition",
|
||||
"from_value": 0,
|
||||
"parentfield": "conditions",
|
||||
"shipping_amount": 50.0,
|
||||
"doctype": "Shipping Rule Condition",
|
||||
"from_value": 0,
|
||||
"parentfield": "conditions",
|
||||
"shipping_amount": 50.0,
|
||||
"to_value": 100
|
||||
},
|
||||
},
|
||||
{
|
||||
"doctype": "Shipping Rule Condition",
|
||||
"from_value": 101,
|
||||
"parentfield": "conditions",
|
||||
"shipping_amount": 100.0,
|
||||
"doctype": "Shipping Rule Condition",
|
||||
"from_value": 101,
|
||||
"parentfield": "conditions",
|
||||
"shipping_amount": 100.0,
|
||||
"to_value": 200
|
||||
},
|
||||
},
|
||||
{
|
||||
"doctype": "Shipping Rule Condition",
|
||||
"from_value": 201,
|
||||
"parentfield": "conditions",
|
||||
"doctype": "Shipping Rule Condition",
|
||||
"from_value": 201,
|
||||
"parentfield": "conditions",
|
||||
"shipping_amount": 0.0
|
||||
}
|
||||
],
|
||||
"territories": [
|
||||
{
|
||||
"doctype": "Applicable Territory",
|
||||
"parentfield": "territories",
|
||||
"territory": "_Test Territory India"
|
||||
}
|
||||
],
|
||||
"countries": [
|
||||
{"country": "India"}
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
"account": "_Test Account Shipping Charges - _TC",
|
||||
"calculate_based_on": "Net Total",
|
||||
"company": "_Test Company",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"doctype": "Shipping Rule",
|
||||
"label": "_Test Shipping Rule - Rest of the World",
|
||||
"name": "_Test Shipping Rule - Rest of the World",
|
||||
"account": "_Test Account Shipping Charges - _TC",
|
||||
"calculate_based_on": "Net Total",
|
||||
"company": "_Test Company",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"doctype": "Shipping Rule",
|
||||
"label": "_Test Shipping Rule - Rest of the World",
|
||||
"name": "_Test Shipping Rule - Rest of the World",
|
||||
"conditions": [
|
||||
{
|
||||
"doctype": "Shipping Rule Condition",
|
||||
"from_value": 0,
|
||||
"parentfield": "conditions",
|
||||
"shipping_amount": 500.0,
|
||||
"doctype": "Shipping Rule Condition",
|
||||
"from_value": 0,
|
||||
"parentfield": "conditions",
|
||||
"shipping_amount": 500.0,
|
||||
"to_value": 1000
|
||||
},
|
||||
},
|
||||
{
|
||||
"doctype": "Shipping Rule Condition",
|
||||
"from_value": 1001,
|
||||
"parentfield": "conditions",
|
||||
"shipping_amount": 1000.0,
|
||||
"doctype": "Shipping Rule Condition",
|
||||
"from_value": 1001,
|
||||
"parentfield": "conditions",
|
||||
"shipping_amount": 1000.0,
|
||||
"to_value": 2000
|
||||
},
|
||||
},
|
||||
{
|
||||
"doctype": "Shipping Rule Condition",
|
||||
"from_value": 2001,
|
||||
"parentfield": "conditions",
|
||||
"doctype": "Shipping Rule Condition",
|
||||
"from_value": 2001,
|
||||
"parentfield": "conditions",
|
||||
"shipping_amount": 1500.0
|
||||
}
|
||||
],
|
||||
"territories": [
|
||||
{
|
||||
"doctype": "Applicable Territory",
|
||||
"parentfield": "territories",
|
||||
"territory": "_Test Territory Rest Of The World"
|
||||
}
|
||||
]
|
||||
],
|
||||
"worldwide_shipping": 1
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -2,26 +2,27 @@
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"creation": "2013-06-20 12:48:38",
|
||||
"creation": "2015-09-17 06:43:22.767534",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Other",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "",
|
||||
"fieldname": "territory",
|
||||
"fieldname": "country",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Territory",
|
||||
"label": "Country",
|
||||
"no_copy": 0,
|
||||
"options": "Territory",
|
||||
"options": "Country",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
@@ -33,18 +34,20 @@
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 1,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-01-01 14:29:58.724652",
|
||||
"modified": "2015-09-17 06:43:22.767534",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Applicable Territory",
|
||||
"module": "Accounts",
|
||||
"name": "Shipping Rule Country",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0
|
||||
"read_only_onload": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See license.txt
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
from frappe.model.document import Document
|
||||
|
||||
class ApplicableTerritory(Document):
|
||||
pass
|
||||
class ShippingRuleCountry(Document):
|
||||
pass
|
||||
60
erpnext/accounts/doctype/tax_rule/tax_rule.js
Normal file
60
erpnext/accounts/doctype/tax_rule/tax_rule.js
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
cur_frm.add_fetch("customer", "customer_group", "customer_group" );
|
||||
cur_frm.add_fetch("supplier", "supplier_type", "supplier_type" );
|
||||
|
||||
cur_frm.toggle_reqd("sales_tax_template", cur_frm.doc.tax_type=="Sales");
|
||||
cur_frm.toggle_reqd("purchase_tax_template", cur_frm.doc.tax_type=="Purchase");
|
||||
|
||||
|
||||
frappe.ui.form.on("Tax Rule", "onload", function(frm) {
|
||||
if(frm.doc.__islocal){
|
||||
frm.set_value("use_for_shopping_cart", 1);
|
||||
}
|
||||
})
|
||||
|
||||
frappe.ui.form.on("Tax Rule", "use_for_shopping_cart", function(frm) {
|
||||
if(!frm.doc.use_for_shopping_cart && (frappe.get_list("Tax Rule", {"use_for_shopping_cart":1}).length == 0)){
|
||||
frappe.model.get_value("Shopping Cart Settings", "Shopping Cart Settings", "enabled", function(docfield) {
|
||||
if(docfield.enabled){
|
||||
frm.set_value("use_for_shopping_cart", 1);
|
||||
frappe.throw(__("Shopping Cart is enabled"));
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
frappe.ui.form.on("Tax Rule", "customer", function(frm) {
|
||||
frappe.call({
|
||||
method:"erpnext.accounts.doctype.tax_rule.tax_rule.get_party_details",
|
||||
args: {
|
||||
"party": frm.doc.customer,
|
||||
"party_type": "customer"
|
||||
},
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
$.each(r.message, function(k, v) {
|
||||
frm.set_value(k, v);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Tax Rule", "supplier", function(frm) {
|
||||
frappe.call({
|
||||
method:"erpnext.accounts.doctype.tax_rule.tax_rule.get_party_details",
|
||||
args: {
|
||||
"party": frm.doc.supplier,
|
||||
"party_type": "supplier"
|
||||
},
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
$.each(r.message, function(k, v) {
|
||||
frm.set_value(k, v);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,27 +1,30 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 0,
|
||||
"creation": "2013-04-30 12:58:38",
|
||||
"autoname": "TR.####",
|
||||
"creation": "2015-08-07 02:33:52.670866",
|
||||
"custom": 0,
|
||||
"description": "System for managing Backups",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "System",
|
||||
"document_type": "Setup",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "setup",
|
||||
"fieldtype": "Section Break",
|
||||
"default": "Sales",
|
||||
"fieldname": "tax_type",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Download Backups",
|
||||
"in_list_view": 1,
|
||||
"label": "Tax Type",
|
||||
"no_copy": 0,
|
||||
"options": "Sales\nPurchase",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
@@ -34,13 +37,13 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "current_backups",
|
||||
"fieldtype": "HTML",
|
||||
"fieldname": "use_for_shopping_cart",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Current Backups",
|
||||
"label": "Use for Shopping Cart",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
@@ -56,56 +59,12 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "",
|
||||
"fieldname": "sync_with_dropbox",
|
||||
"fieldtype": "Section Break",
|
||||
"fieldname": "column_break_1",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Sync with Dropbox",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "backup_right_now",
|
||||
"fieldtype": "Button",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Backup Right Now",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "send_backups_to_dropbox",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Send Backups to Dropbox",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
@@ -121,18 +80,18 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "send_backups_to_dropbox",
|
||||
"description": "Note: Backups and files are not deleted from Dropbox, you will have to delete them manually.",
|
||||
"fieldname": "upload_backups_to_dropbox",
|
||||
"fieldtype": "Select",
|
||||
"depends_on": "eval:doc.tax_type==\"Sales\"",
|
||||
"fieldname": "sales_tax_template",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Upload Backups to Dropbox",
|
||||
"label": "Sales Tax Template",
|
||||
"no_copy": 0,
|
||||
"options": "Never\nWeekly\nDaily",
|
||||
"options": "Sales Taxes and Charges Template",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
@@ -145,17 +104,18 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "send_backups_to_dropbox",
|
||||
"description": "Email ids separated by commas.",
|
||||
"fieldname": "send_notifications_to",
|
||||
"fieldtype": "Data",
|
||||
"depends_on": "eval:doc.tax_type==\"Purchase\"",
|
||||
"fieldname": "purchase_tax_template",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Send Notifications To",
|
||||
"label": "Purchase Tax Template",
|
||||
"no_copy": 0,
|
||||
"options": "Purchase Taxes and Charges Template",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
@@ -168,123 +128,16 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "dropbox_access_key",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Dropbox Access Key",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "dropbox_access_secret",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Dropbox Access Secret",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "dropbox_access_allowed",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Dropbox Access Allowed",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "send_backups_to_dropbox",
|
||||
"fieldname": "allow_dropbox_access",
|
||||
"fieldtype": "Button",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Allow Dropbox Access",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"description": "Note: Backups and files are not deleted from Google Drive, you will have to delete them manually.",
|
||||
"fieldname": "sync_with_gdrive",
|
||||
"fieldname": "filters",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Sync with Google Drive",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "upload_backups_to_gdrive",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Upload Backups to Google Drive",
|
||||
"label": "Filters",
|
||||
"no_copy": 0,
|
||||
"options": "Never\nDaily\nWeekly",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
@@ -297,15 +150,18 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "allow_gdrive_access",
|
||||
"fieldtype": "Button",
|
||||
"depends_on": "eval:doc.tax_type==\"Sales\"",
|
||||
"fieldname": "customer",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Allow Google Drive Access",
|
||||
"label": "Customer",
|
||||
"no_copy": 0,
|
||||
"options": "Customer",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
@@ -318,15 +174,40 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "verification_code",
|
||||
"depends_on": "eval:doc.tax_type==\"Purchase\"",
|
||||
"fieldname": "supplier",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Supplier",
|
||||
"no_copy": 0,
|
||||
"options": "Supplier",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "billing_city",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Enter Verification Code",
|
||||
"label": "Billing City",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
@@ -339,15 +220,16 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "validate_gdrive",
|
||||
"fieldtype": "Button",
|
||||
"fieldname": "billing_state",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Validate",
|
||||
"label": "Billing State",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
@@ -360,17 +242,19 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "gdrive_access_allowed",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"fieldname": "billing_country",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Google Drive Access Allowed",
|
||||
"label": "Billing Country",
|
||||
"no_copy": 0,
|
||||
"options": "Country",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
@@ -381,17 +265,17 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "gdrive_credentials",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 1,
|
||||
"fieldname": "column_break_2",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Credentials",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
@@ -402,17 +286,66 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "database_folder_id",
|
||||
"depends_on": "eval:doc.tax_type==\"Sales\"",
|
||||
"fieldname": "customer_group",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Customer Group",
|
||||
"no_copy": 0,
|
||||
"options": "Customer Group",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"depends_on": "eval:doc.tax_type==\"Purchase\"",
|
||||
"fieldname": "supplier_type",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Supplier Type",
|
||||
"no_copy": 0,
|
||||
"options": "Supplier Type",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "shipping_city",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Database Folder ID",
|
||||
"label": "Shipping City",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
@@ -423,17 +356,216 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "files_folder_id",
|
||||
"fieldname": "shipping_state",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Files Folder ID",
|
||||
"label": "Shipping State",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 1,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "shipping_country",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Shipping Country",
|
||||
"no_copy": 0,
|
||||
"options": "Country",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "section_break_4",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Validity",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "From Date",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_7",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "To Date",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "section_break_6",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"default": "1",
|
||||
"fieldname": "priority",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Priority",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_20",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"label": "Company",
|
||||
"no_copy": 0,
|
||||
"options": "Company",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
@@ -443,17 +575,16 @@
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "icon-cloud-upload",
|
||||
"idx": 1,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 1,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-05-26 04:54:10.193573",
|
||||
"modified": "2015-09-24 07:59:06.502058",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Backup Manager",
|
||||
"module": "Accounts",
|
||||
"name": "Tax Rule",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
@@ -461,16 +592,16 @@
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 0,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 0,
|
||||
"role": "System Manager",
|
||||
"report": 1,
|
||||
"role": "Accounts Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
@@ -478,5 +609,7 @@
|
||||
}
|
||||
],
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0
|
||||
"read_only_onload": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
||||
132
erpnext/accounts/doctype/tax_rule/tax_rule.py
Normal file
132
erpnext/accounts/doctype/tax_rule/tax_rule.py
Normal file
@@ -0,0 +1,132 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cstr
|
||||
|
||||
class IncorrectCustomerGroup(frappe.ValidationError): pass
|
||||
class IncorrectSupplierType(frappe.ValidationError): pass
|
||||
class ConflictingTaxRule(frappe.ValidationError): pass
|
||||
|
||||
class TaxRule(Document):
|
||||
def __setup__(self):
|
||||
self.flags.ignore_these_exceptions_in_test = [ConflictingTaxRule]
|
||||
|
||||
def validate(self):
|
||||
self.validate_tax_template()
|
||||
self.validate_date()
|
||||
self.validate_filters()
|
||||
|
||||
def validate_tax_template(self):
|
||||
if self.tax_type== "Sales":
|
||||
self.purchase_tax_template = self.supplier = self.supplier_type= None
|
||||
if self.customer:
|
||||
self.customer_group = None
|
||||
|
||||
else:
|
||||
self.sales_tax_template= self.customer = self.customer_group= None
|
||||
|
||||
if self.supplier:
|
||||
self.supplier_type = None
|
||||
|
||||
if not (self.sales_tax_template or self.purchase_tax_template):
|
||||
frappe.throw(_("Tax Template is mandatory."))
|
||||
|
||||
def validate_date(self):
|
||||
if self.from_date and self.to_date and self.from_date > self.to_date:
|
||||
frappe.throw(_("From Date cannot be greater than To Date"))
|
||||
|
||||
def validate_filters(self):
|
||||
filters = {
|
||||
"tax_type": self.tax_type,
|
||||
"customer": self.customer,
|
||||
"customer_group": self.customer_group,
|
||||
"supplier": self.supplier,
|
||||
"supplier_type": self.supplier_type,
|
||||
"billing_city": self.billing_city,
|
||||
"billing_state": self.billing_state,
|
||||
"billing_country": self.billing_country,
|
||||
"shipping_city": self.shipping_city,
|
||||
"shipping_state": self.shipping_state,
|
||||
"shipping_country": self.shipping_country,
|
||||
"company": self.company
|
||||
}
|
||||
|
||||
conds=""
|
||||
for d in filters:
|
||||
if conds:
|
||||
conds += " and "
|
||||
conds += """ifnull({0}, '') = '{1}'""".format(d, frappe.db.escape(cstr(filters[d])))
|
||||
|
||||
if self.from_date and self.to_date:
|
||||
conds += """ and ((from_date > '{from_date}' and from_date < '{to_date}') or
|
||||
(to_date > '{from_date}' and to_date < '{to_date}') or
|
||||
('{from_date}' > from_date and '{from_date}' < to_date) or
|
||||
('{from_date}' = from_date and '{to_date}' = to_date))""".format(from_date=self.from_date, to_date=self.to_date)
|
||||
|
||||
elif self.from_date and not self.to_date:
|
||||
conds += """ and to_date > '{from_date}'""".format(from_date = self.from_date)
|
||||
|
||||
elif self.to_date and not self.from_date:
|
||||
conds += """ and from_date < '{to_date}'""".format(to_date = self.to_date)
|
||||
|
||||
tax_rule = frappe.db.sql("select name, priority \
|
||||
from `tabTax Rule` where {0} and name != '{1}'".format(conds, self.name), as_dict=1)
|
||||
|
||||
if tax_rule:
|
||||
if tax_rule[0].priority == self.priority:
|
||||
frappe.throw(_("Tax Rule Conflicts with {0}".format(tax_rule[0].name)), ConflictingTaxRule)
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_party_details(party, party_type, args=None):
|
||||
out = {}
|
||||
if args:
|
||||
billing_filters= {"name": args.get("billing_address")}
|
||||
shipping_filters= {"name": args.get("shipping_address")}
|
||||
else:
|
||||
billing_filters= {party_type: party, "is_primary_address": 1}
|
||||
shipping_filters= {party_type:party, "is_shipping_address": 1}
|
||||
|
||||
billing_address= frappe.get_all("Address", fields=["city", "state", "country"], filters= billing_filters)
|
||||
shipping_address= frappe.get_all("Address", fields=["city", "state", "country"], filters= shipping_filters)
|
||||
|
||||
if billing_address:
|
||||
out["billing_city"]= billing_address[0].city
|
||||
out["billing_state"]= billing_address[0].state
|
||||
out["billing_country"]= billing_address[0].country
|
||||
|
||||
if shipping_address:
|
||||
out["shipping_city"]= shipping_address[0].city
|
||||
out["shipping_state"]= shipping_address[0].state
|
||||
out["shipping_country"]= shipping_address[0].country
|
||||
|
||||
return out
|
||||
|
||||
def get_tax_template(posting_date, args):
|
||||
"""Get matching tax rule"""
|
||||
args = frappe._dict(args)
|
||||
conditions = []
|
||||
|
||||
for key, value in args.iteritems():
|
||||
if key in "use_for_shopping_cart":
|
||||
conditions.append("use_for_shopping_cart = {0}".format(1 if value else 0))
|
||||
else:
|
||||
conditions.append("ifnull({0}, '') in ('', '{1}')".format(key, frappe.db.escape(cstr(value))))
|
||||
|
||||
matching = frappe.db.sql("""select * from `tabTax Rule`
|
||||
where {0}""".format(" and ".join(conditions)), as_dict = True)
|
||||
|
||||
if not matching:
|
||||
return None
|
||||
|
||||
for rule in matching:
|
||||
rule.no_of_keys_matched = 0
|
||||
for key in args:
|
||||
if rule.get(key): rule.no_of_keys_matched += 1
|
||||
|
||||
rule = sorted(matching, lambda b, a: cmp(a.no_of_keys_matched, b.no_of_keys_matched) or cmp(a.priority, b.priority))[0]
|
||||
return rule.sales_tax_template or rule.purchase_tax_template
|
||||
28
erpnext/accounts/doctype/tax_rule/test_records.json
Normal file
28
erpnext/accounts/doctype/tax_rule/test_records.json
Normal file
@@ -0,0 +1,28 @@
|
||||
[
|
||||
{
|
||||
"doctype": "Tax Rule",
|
||||
"tax_type" : "Sales",
|
||||
"sales_tax_template": "_Test Tax 1",
|
||||
"use_for_shopping_cart": 1,
|
||||
"billing_city": "_Test City",
|
||||
"billing_state": "Test State",
|
||||
"billing_country": "India",
|
||||
"shipping_city": "_Test City",
|
||||
"shipping_country": "India",
|
||||
"priority": 1,
|
||||
"company": "_Test Company"
|
||||
},
|
||||
{
|
||||
"doctype": "Tax Rule",
|
||||
"tax_type" : "Sales",
|
||||
"sales_tax_template": "_Test Tax 2",
|
||||
"use_for_shopping_cart": 0,
|
||||
"billing_city": "_Test City",
|
||||
"billing_country": "India",
|
||||
"shipping_city": "_Test City",
|
||||
"shipping_state": "Test State",
|
||||
"shipping_country": "India",
|
||||
"priority": 2,
|
||||
"company": "_Test Company"
|
||||
}
|
||||
]
|
||||
130
erpnext/accounts/doctype/tax_rule/test_tax_rule.py
Normal file
130
erpnext/accounts/doctype/tax_rule/test_tax_rule.py
Normal file
@@ -0,0 +1,130 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
from erpnext.accounts.doctype.tax_rule.tax_rule import IncorrectCustomerGroup, IncorrectSupplierType, ConflictingTaxRule, get_tax_template
|
||||
|
||||
test_records = frappe.get_test_records('Tax Rule')
|
||||
|
||||
class TestTaxRule(unittest.TestCase):
|
||||
def setUp(self):
|
||||
frappe.db.sql("delete from `tabTax Rule` where use_for_shopping_cart <> 1")
|
||||
|
||||
def test_conflict(self):
|
||||
tax_rule1 = make_tax_rule(customer= "_Test Customer",
|
||||
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1)
|
||||
tax_rule1.save()
|
||||
|
||||
tax_rule2 = make_tax_rule(customer= "_Test Customer",
|
||||
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1)
|
||||
|
||||
self.assertRaises(ConflictingTaxRule, tax_rule2.save)
|
||||
|
||||
def test_conflict_with_non_overlapping_dates(self):
|
||||
tax_rule1 = make_tax_rule(customer= "_Test Customer",
|
||||
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1, from_date = "2015-01-01")
|
||||
tax_rule1.save()
|
||||
|
||||
tax_rule2 = make_tax_rule(customer= "_Test Customer",
|
||||
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1, to_date = "2013-01-01")
|
||||
|
||||
tax_rule2.save()
|
||||
self.assertTrue(tax_rule2.name)
|
||||
|
||||
def test_conflict_with_overlapping_dates(self):
|
||||
tax_rule1 = make_tax_rule(customer= "_Test Customer",
|
||||
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1, from_date = "2015-01-01", to_date = "2015-01-05")
|
||||
tax_rule1.save()
|
||||
|
||||
tax_rule2 = make_tax_rule(customer= "_Test Customer",
|
||||
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1, from_date = "2015-01-03", to_date = "2015-01-09")
|
||||
|
||||
self.assertRaises(ConflictingTaxRule, tax_rule2.save)
|
||||
|
||||
def test_tax_template(self):
|
||||
tax_rule = make_tax_rule()
|
||||
self.assertEquals(tax_rule.purchase_tax_template, None)
|
||||
|
||||
|
||||
def test_select_tax_rule_based_on_customer(self):
|
||||
make_tax_rule(customer= "_Test Customer",
|
||||
sales_tax_template = "_Test Sales Taxes and Charges Template", save=1)
|
||||
|
||||
make_tax_rule(customer= "_Test Customer 1",
|
||||
sales_tax_template = "_Test Sales Taxes and Charges Template 1", save=1)
|
||||
|
||||
make_tax_rule(customer= "_Test Customer 2",
|
||||
sales_tax_template = "_Test Sales Taxes and Charges Template 2", save=1)
|
||||
|
||||
self.assertEquals(get_tax_template("2015-01-01", {"customer":"_Test Customer 2"}),
|
||||
"_Test Sales Taxes and Charges Template 2")
|
||||
|
||||
def test_select_tax_rule_based_on_better_match(self):
|
||||
make_tax_rule(customer= "_Test Customer", billing_city = "Test City", billing_state = "Test State",
|
||||
sales_tax_template = "_Test Sales Taxes and Charges Template", save=1)
|
||||
|
||||
make_tax_rule(customer= "_Test Customer", billing_city = "Test City1", billing_state = "Test State",
|
||||
sales_tax_template = "_Test Sales Taxes and Charges Template 1", save=1)
|
||||
|
||||
self.assertEquals(get_tax_template("2015-01-01", {"customer":"_Test Customer", "billing_city": "Test City", "billing_state": "Test State"}),
|
||||
"_Test Sales Taxes and Charges Template")
|
||||
|
||||
def test_select_tax_rule_based_on_state_match(self):
|
||||
make_tax_rule(customer= "_Test Customer", shipping_state = "Test State",
|
||||
sales_tax_template = "_Test Sales Taxes and Charges Template", save=1)
|
||||
|
||||
make_tax_rule(customer= "_Test Customer", shipping_state = "Test State12",
|
||||
sales_tax_template = "_Test Sales Taxes and Charges Template 1", priority=2, save=1)
|
||||
|
||||
self.assertEquals(get_tax_template("2015-01-01", {"customer":"_Test Customer", "shipping_state": "Test State"}),
|
||||
"_Test Sales Taxes and Charges Template")
|
||||
|
||||
def test_select_tax_rule_based_on_better_priority(self):
|
||||
make_tax_rule(customer= "_Test Customer", billing_city = "Test City",
|
||||
sales_tax_template = "_Test Sales Taxes and Charges Template", priority=1, save=1)
|
||||
|
||||
make_tax_rule(customer= "_Test Customer", billing_city = "Test City",
|
||||
sales_tax_template = "_Test Sales Taxes and Charges Template 1", priority=2, save=1)
|
||||
|
||||
self.assertEquals(get_tax_template("2015-01-01", {"customer":"_Test Customer", "billing_city": "Test City"}),
|
||||
"_Test Sales Taxes and Charges Template 1")
|
||||
|
||||
def test_select_tax_rule_based_cross_matching_keys(self):
|
||||
make_tax_rule(customer= "_Test Customer", billing_city = "Test City",
|
||||
sales_tax_template = "_Test Sales Taxes and Charges Template", save=1)
|
||||
|
||||
make_tax_rule(customer= "_Test Customer 1", billing_city = "Test City 1",
|
||||
sales_tax_template = "_Test Sales Taxes and Charges Template 1", save=1)
|
||||
|
||||
self.assertEquals(get_tax_template("2015-01-01", {"customer":"_Test Customer", "billing_city": "Test City 1"}),
|
||||
None)
|
||||
|
||||
def test_select_tax_rule_based_cross_partially_keys(self):
|
||||
make_tax_rule(customer= "_Test Customer", billing_city = "Test City",
|
||||
sales_tax_template = "_Test Sales Taxes and Charges Template", save=1)
|
||||
|
||||
make_tax_rule(billing_city = "Test City 1",
|
||||
sales_tax_template = "_Test Sales Taxes and Charges Template 1", save=1)
|
||||
|
||||
self.assertEquals(get_tax_template("2015-01-01", {"customer":"_Test Customer", "billing_city": "Test City 1"}),
|
||||
"_Test Sales Taxes and Charges Template 1")
|
||||
|
||||
|
||||
def make_tax_rule(**args):
|
||||
args = frappe._dict(args)
|
||||
|
||||
tax_rule = frappe.new_doc("Tax Rule")
|
||||
|
||||
for key, val in args.iteritems():
|
||||
if key != "save":
|
||||
tax_rule.set(key, val)
|
||||
|
||||
tax_rule.company = args.company or "_Test Company"
|
||||
|
||||
if args.save:
|
||||
tax_rule.insert()
|
||||
|
||||
return tax_rule
|
||||
@@ -10,9 +10,9 @@ from frappe.defaults import get_user_permissions
|
||||
from frappe.utils import add_days, getdate, formatdate, get_first_day, date_diff
|
||||
from erpnext.utilities.doctype.address.address import get_address_display
|
||||
from erpnext.utilities.doctype.contact.contact import get_contact_details
|
||||
from erpnext.exceptions import InvalidAccountCurrency
|
||||
|
||||
class InvalidCurrency(frappe.ValidationError): pass
|
||||
class InvalidAccountCurrency(frappe.ValidationError): pass
|
||||
class DuplicatePartyAccountError(frappe.ValidationError): pass
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_party_details(party=None, account=None, party_type="Customer", company=None,
|
||||
@@ -20,7 +20,7 @@ def get_party_details(party=None, account=None, party_type="Customer", company=N
|
||||
|
||||
if not party:
|
||||
return {}
|
||||
|
||||
|
||||
if not frappe.db.exists(party_type, party):
|
||||
frappe.throw(_("{0}: {1} does not exists").format(party_type, party))
|
||||
|
||||
@@ -29,7 +29,7 @@ def get_party_details(party=None, account=None, party_type="Customer", company=N
|
||||
|
||||
def _get_party_details(party=None, account=None, party_type="Customer", company=None,
|
||||
posting_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False):
|
||||
|
||||
|
||||
out = frappe._dict(set_account_and_due_date(party, account, party_type, company, posting_date, doctype))
|
||||
|
||||
party = out[party_type.lower()]
|
||||
@@ -43,6 +43,7 @@ def _get_party_details(party=None, account=None, party_type="Customer", company=
|
||||
set_contact_details(out, party, party_type)
|
||||
set_other_values(out, party, party_type)
|
||||
set_price_list(out, party, party_type, price_list)
|
||||
out["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company, out.customer_group, out.supplier_type)
|
||||
|
||||
if not out.get("currency"):
|
||||
out["currency"] = currency
|
||||
@@ -50,9 +51,7 @@ def _get_party_details(party=None, account=None, party_type="Customer", company=
|
||||
# sales team
|
||||
if party_type=="Customer":
|
||||
out["sales_team"] = [{
|
||||
"sales_person": d.sales_person,
|
||||
"sales_designation": d.sales_designation,
|
||||
"allocated_percentage": d.allocated_percentage
|
||||
"sales_person": d.sales_person
|
||||
} for d in party.get("sales_team")]
|
||||
|
||||
return out
|
||||
@@ -99,11 +98,24 @@ def set_other_values(out, party, party_type):
|
||||
out[f] = party.get(f)
|
||||
|
||||
# fields prepended with default in Customer doctype
|
||||
for f in ['currency', 'taxes_and_charges'] \
|
||||
for f in ['currency'] \
|
||||
+ (['sales_partner', 'commission_rate'] if party_type=="Customer" else []):
|
||||
if party.get("default_" + f):
|
||||
out[f] = party.get("default_" + f)
|
||||
|
||||
def get_default_price_list(party):
|
||||
"""Return default price list for party (Document object)"""
|
||||
if party.default_price_list:
|
||||
return party.default_price_list
|
||||
|
||||
if party.doctype == "Customer":
|
||||
price_list = frappe.db.get_value("Customer Group",
|
||||
party.customer_group, "default_price_list")
|
||||
if price_list:
|
||||
return price_list
|
||||
|
||||
return None
|
||||
|
||||
def set_price_list(out, party, party_type, given_price_list):
|
||||
# price list
|
||||
price_list = filter(None, get_user_permissions().get("Price List", []))
|
||||
@@ -111,11 +123,7 @@ def set_price_list(out, party, party_type, given_price_list):
|
||||
price_list = price_list[0] if len(price_list)==1 else None
|
||||
|
||||
if not price_list:
|
||||
price_list = party.default_price_list
|
||||
|
||||
if not price_list and party_type=="Customer":
|
||||
price_list = frappe.db.get_value("Customer Group",
|
||||
party.customer_group, "default_price_list")
|
||||
price_list = get_default_price_list(party)
|
||||
|
||||
if not price_list:
|
||||
price_list = given_price_list
|
||||
@@ -134,7 +142,7 @@ def set_account_and_due_date(party, account, party_type, company, posting_date,
|
||||
}
|
||||
|
||||
if party:
|
||||
account = get_party_account(company, party, party_type)
|
||||
account = get_party_account(party_type, party, company)
|
||||
|
||||
account_fieldname = "debit_to" if party_type=="Customer" else "credit_to"
|
||||
|
||||
@@ -144,69 +152,22 @@ def set_account_and_due_date(party, account, party_type, company, posting_date,
|
||||
"due_date": get_due_date(posting_date, party_type, party, company)
|
||||
}
|
||||
return out
|
||||
|
||||
def validate_accounting_currency(party):
|
||||
company_currency = get_company_currency()
|
||||
|
||||
# set party account currency
|
||||
if not party.party_account_currency:
|
||||
if party.default_currency:
|
||||
party.party_account_currency = party.default_currency
|
||||
elif len(set(company_currency.values())) == 1:
|
||||
party.party_account_currency = company_currency.values()[0]
|
||||
|
||||
party_account_currency_in_db = frappe.db.get_value(party.doctype, party.name, "party_account_currency")
|
||||
if party_account_currency_in_db != party.party_account_currency:
|
||||
existing_gle = frappe.db.get_value("GL Entry", {"party_type": party.doctype,
|
||||
"party": party.name}, ["name", "account_currency"], as_dict=1)
|
||||
if existing_gle:
|
||||
if party_account_currency_in_db:
|
||||
frappe.throw(_("Accounting Currency cannot be changed, as GL Entry exists for this {0}")
|
||||
.format(party.doctype), InvalidCurrency)
|
||||
else:
|
||||
party.party_account_currency = existing_gle.account_currency
|
||||
|
||||
|
||||
def validate_party_account(party):
|
||||
company_currency = get_company_currency()
|
||||
if party.party_account_currency:
|
||||
companies_with_different_currency = []
|
||||
for company, currency in company_currency.items():
|
||||
if currency != party.party_account_currency:
|
||||
companies_with_different_currency.append(company)
|
||||
|
||||
for d in party.get("accounts"):
|
||||
if d.company in companies_with_different_currency:
|
||||
companies_with_different_currency.remove(d.company)
|
||||
|
||||
selected_account_currency = frappe.db.get_value("Account", d.account, "account_currency")
|
||||
if selected_account_currency != party.party_account_currency:
|
||||
frappe.throw(_("Account {0} is invalid, account currency must be {1}")
|
||||
.format(d.account, selected_account_currency), InvalidAccountCurrency)
|
||||
|
||||
if companies_with_different_currency:
|
||||
frappe.msgprint(_("Please mention Default {0} Account for the following companies, as accounting currency is different from company's default currency: {1}")
|
||||
.format(
|
||||
"Receivable" if party.doctype=="Customer" else "Payable",
|
||||
"\n" + "\n".join(companies_with_different_currency)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def get_company_currency():
|
||||
company_currency = frappe._dict()
|
||||
for d in frappe.get_all("Company", fields=["name", "default_currency"]):
|
||||
company_currency.setdefault(d.name, d.default_currency)
|
||||
|
||||
|
||||
return company_currency
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_party_account(company, party, party_type):
|
||||
def get_party_account(party_type, party, company):
|
||||
"""Returns the account for the given `party`.
|
||||
Will first search in party (Customer / Supplier) record, if not found,
|
||||
will search in group (Customer Group / Supplier Type),
|
||||
finally will return default."""
|
||||
if not company:
|
||||
frappe.throw(_("Please select company first."))
|
||||
frappe.throw(_("Please select a Company"))
|
||||
|
||||
if party:
|
||||
account = frappe.db.get_value("Party Account",
|
||||
@@ -224,6 +185,42 @@ def get_party_account(company, party, party_type):
|
||||
|
||||
return account
|
||||
|
||||
def get_party_account_currency(party_type, party, company):
|
||||
def generator():
|
||||
party_account = get_party_account(party_type, party, company)
|
||||
return frappe.db.get_value("Account", party_account, "account_currency")
|
||||
|
||||
return frappe.local_cache("party_account_currency", (party_type, party, company), generator)
|
||||
|
||||
def get_party_gle_currency(party_type, party, company):
|
||||
def generator():
|
||||
existing_gle_currency = frappe.db.sql("""select account_currency from `tabGL Entry`
|
||||
where docstatus=1 and company=%(company)s and party_type=%(party_type)s and party=%(party)s
|
||||
limit 1""", { "company": company, "party_type": party_type, "party": party })
|
||||
|
||||
return existing_gle_currency[0][0] if existing_gle_currency else None
|
||||
|
||||
return frappe.local_cache("party_gle_currency", (party_type, party, company), generator)
|
||||
|
||||
def validate_party_gle_currency(party_type, party, company):
|
||||
"""Validate party account currency with existing GL Entry's currency"""
|
||||
party_account_currency = get_party_account_currency(party_type, party, company)
|
||||
existing_gle_currency = get_party_gle_currency(party_type, party, company)
|
||||
|
||||
if existing_gle_currency and party_account_currency != existing_gle_currency:
|
||||
frappe.throw(_("Accounting Entry for {0}: {1} can only be made in currency: {2}")
|
||||
.format(party_type, party, existing_gle_currency), InvalidAccountCurrency)
|
||||
|
||||
def validate_party_accounts(doc):
|
||||
companies = []
|
||||
|
||||
for account in doc.get("accounts"):
|
||||
if account.company in companies:
|
||||
frappe.throw(_("There can only be 1 Account per Company in {0} {1}").format(doc.doctype, doc.name),
|
||||
DuplicatePartyAccountError)
|
||||
else:
|
||||
companies.append(account.company)
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_due_date(posting_date, party_type, party, company):
|
||||
"""Set Due Date = Posting Date + Credit Days"""
|
||||
@@ -240,7 +237,7 @@ def get_due_date(posting_date, party_type, party, company):
|
||||
credit_days = get_credit_days(party_type, party, company)
|
||||
if credit_days:
|
||||
due_date = add_days(posting_date, credit_days)
|
||||
|
||||
|
||||
return due_date
|
||||
|
||||
def get_credit_days(party_type, party, company):
|
||||
@@ -248,30 +245,60 @@ def get_credit_days(party_type, party, company):
|
||||
if party_type == "Customer":
|
||||
credit_days_based_on, credit_days, customer_group = \
|
||||
frappe.db.get_value(party_type, party, ["credit_days_based_on", "credit_days", "customer_group"])
|
||||
|
||||
|
||||
if not credit_days_based_on:
|
||||
credit_days_based_on, credit_days = \
|
||||
frappe.db.get_value("Customer Group", customer_group, ["credit_days_based_on", "credit_days"]) \
|
||||
or frappe.db.get_value("Company", company, ["credit_days_based_on", "credit_days"])
|
||||
|
||||
|
||||
return credit_days_based_on, credit_days
|
||||
else:
|
||||
credit_days, supplier_type = frappe.db.get_value(party_type, party, ["credit_days", "supplier_type"])
|
||||
if not credit_days:
|
||||
credit_days = frappe.db.get_value("Supplier Type", supplier_type, "credit_days") \
|
||||
or frappe.db.get_value("Company", company, "credit_days")
|
||||
|
||||
|
||||
return credit_days
|
||||
|
||||
|
||||
def validate_due_date(posting_date, due_date, party_type, party, company):
|
||||
if getdate(due_date) < getdate(posting_date):
|
||||
frappe.throw(_("Due Date cannot be before Posting Date"))
|
||||
else:
|
||||
default_due_date = get_due_date(posting_date, party_type, party, company)
|
||||
if not default_due_date:
|
||||
return
|
||||
|
||||
if default_due_date != posting_date and getdate(due_date) > getdate(default_due_date):
|
||||
is_credit_controller = frappe.db.get_single_value("Accounts Settings", "credit_controller") in frappe.get_roles()
|
||||
if is_credit_controller:
|
||||
msgprint(_("Note: Due / Reference Date exceeds allowed customer credit days by {0} day(s)")
|
||||
.format(date_diff(due_date, default_due_date)))
|
||||
else:
|
||||
frappe.throw(_("Due / Reference Date cannot be after {0}").format(formatdate(default_due_date)))
|
||||
frappe.throw(_("Due / Reference Date cannot be after {0}").format(formatdate(default_due_date)))
|
||||
|
||||
@frappe.whitelist()
|
||||
def set_taxes(party, party_type, posting_date, company, customer_group=None, supplier_type=None,
|
||||
billing_address=None, shipping_address=None, use_for_shopping_cart=None):
|
||||
from erpnext.accounts.doctype.tax_rule.tax_rule import get_tax_template, get_party_details
|
||||
args = {
|
||||
party_type.lower(): party,
|
||||
"customer_group": customer_group,
|
||||
"supplier_type": supplier_type,
|
||||
"company": company
|
||||
}
|
||||
|
||||
if billing_address or shipping_address:
|
||||
args.update(get_party_details(party, party_type, {"billing_address": billing_address, \
|
||||
"shipping_address": shipping_address }))
|
||||
else:
|
||||
args.update(get_party_details(party, party_type))
|
||||
|
||||
if party_type=="Customer":
|
||||
args.update({"tax_type": "Sales"})
|
||||
else:
|
||||
args.update({"tax_type": "Purchase"})
|
||||
|
||||
if use_for_shopping_cart:
|
||||
args.update({"use_for_shopping_cart": use_for_shopping_cart})
|
||||
|
||||
return get_tax_template(posting_date, args)
|
||||
|
||||
@@ -209,7 +209,8 @@ class ReceivablePayableReport(object):
|
||||
self.gl_entries = frappe.db.sql("""select name, posting_date, account, party_type, party,
|
||||
voucher_type, voucher_no, against_voucher_type, against_voucher, account_currency, remarks, {0}
|
||||
from `tabGL Entry`
|
||||
where docstatus < 2 and party_type=%s {1} order by posting_date, party"""
|
||||
where docstatus < 2 and party_type=%s and ifnull(party, '') != '' {1}
|
||||
order by posting_date, party"""
|
||||
.format(select_fields, conditions), values, as_dict=True)
|
||||
|
||||
return self.gl_entries
|
||||
|
||||
@@ -5,6 +5,7 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.utils import flt, getdate, cstr
|
||||
from frappe import _
|
||||
from erpnext.accounts.utils import get_account_currency
|
||||
|
||||
def execute(filters=None):
|
||||
account_details = {}
|
||||
@@ -12,10 +13,10 @@ def execute(filters=None):
|
||||
account_details.setdefault(acc.name, acc)
|
||||
|
||||
validate_filters(filters, account_details)
|
||||
|
||||
|
||||
validate_party(filters)
|
||||
|
||||
filters = set_account_currency(filters)
|
||||
|
||||
filters = set_account_currency(filters)
|
||||
|
||||
columns = get_columns(filters)
|
||||
|
||||
@@ -46,49 +47,49 @@ def validate_party(filters):
|
||||
frappe.throw(_("To filter based on Party, select Party Type first"))
|
||||
elif not frappe.db.exists(party_type, party):
|
||||
frappe.throw(_("Invalid {0}: {1}").format(party_type, party))
|
||||
|
||||
|
||||
def set_account_currency(filters):
|
||||
if not (filters.get("account") or filters.get("party")):
|
||||
return filters
|
||||
else:
|
||||
filters["company_currency"] = frappe.db.get_value("Company", filters.company, "default_currency")
|
||||
account_currency = None
|
||||
|
||||
|
||||
if filters.get("account"):
|
||||
account_currency = frappe.db.get_value("Account", filters.account, "account_currency")
|
||||
account_currency = get_account_currency(filters.account)
|
||||
elif filters.get("party"):
|
||||
gle_currency = frappe.db.get_value("GL Entry", {"party_type": filters.party_type,
|
||||
gle_currency = frappe.db.get_value("GL Entry", {"party_type": filters.party_type,
|
||||
"party": filters.party, "company": filters.company}, "account_currency")
|
||||
if gle_currency:
|
||||
account_currency = gle_currency
|
||||
else:
|
||||
account_currency = frappe.db.get_value(filters.party_type, filters.party, "default_currency")
|
||||
|
||||
|
||||
filters["account_currency"] = account_currency or filters.company_currency
|
||||
|
||||
|
||||
if filters.account_currency != filters.company_currency:
|
||||
filters["show_in_account_currency"] = 1
|
||||
|
||||
|
||||
return filters
|
||||
|
||||
|
||||
def get_columns(filters):
|
||||
columns = [
|
||||
_("Posting Date") + ":Date:90", _("Account") + ":Link/Account:200",
|
||||
_("Debit") + ":Float:100", _("Credit") + ":Float:100"
|
||||
]
|
||||
|
||||
|
||||
if filters.get("show_in_account_currency"):
|
||||
columns += [
|
||||
_("Debit") + " (" + filters.account_currency + ")" + ":Float:100",
|
||||
_("Debit") + " (" + filters.account_currency + ")" + ":Float:100",
|
||||
_("Credit") + " (" + filters.account_currency + ")" + ":Float:100"
|
||||
]
|
||||
|
||||
|
||||
columns += [
|
||||
_("Voucher Type") + "::120", _("Voucher No") + ":Dynamic Link/Voucher Type:160",
|
||||
_("Against Account") + "::120", _("Party Type") + "::80", _("Party") + "::150",
|
||||
_("Cost Center") + ":Link/Cost Center:100", _("Remarks") + "::400"
|
||||
]
|
||||
|
||||
|
||||
return columns
|
||||
|
||||
def get_result(filters, account_details):
|
||||
@@ -101,21 +102,21 @@ def get_result(filters, account_details):
|
||||
return result
|
||||
|
||||
def get_gl_entries(filters):
|
||||
select_fields = """, sum(ifnull(debit_in_account_currency, 0)) as debit_in_account_currency,
|
||||
select_fields = """, sum(ifnull(debit_in_account_currency, 0)) as debit_in_account_currency,
|
||||
sum(ifnull(credit_in_account_currency, 0)) as credit_in_account_currency""" \
|
||||
if filters.get("show_in_account_currency") else ""
|
||||
|
||||
|
||||
group_by_condition = "group by voucher_type, voucher_no, account, cost_center" \
|
||||
if filters.get("group_by_voucher") else "group by name"
|
||||
|
||||
gl_entries = frappe.db.sql("""select posting_date, account, party_type, party,
|
||||
sum(ifnull(debit, 0)) as debit, sum(ifnull(credit, 0)) as credit,
|
||||
sum(ifnull(debit, 0)) as debit, sum(ifnull(credit, 0)) as credit,
|
||||
voucher_type, voucher_no, cost_center, remarks, against, is_opening {select_fields}
|
||||
from `tabGL Entry`
|
||||
where company=%(company)s {conditions}
|
||||
{group_by_condition}
|
||||
order by posting_date, account"""\
|
||||
.format(select_fields=select_fields, conditions=get_conditions(filters),
|
||||
.format(select_fields=select_fields, conditions=get_conditions(filters),
|
||||
group_by_condition=group_by_condition), filters, as_dict=1)
|
||||
|
||||
return gl_entries
|
||||
@@ -135,7 +136,7 @@ def get_conditions(filters):
|
||||
|
||||
if filters.get("party"):
|
||||
conditions.append("party=%(party)s")
|
||||
|
||||
|
||||
if not (filters.get("account") or filters.get("party") or filters.get("group_by_account")):
|
||||
conditions.append("posting_date >=%(from_date)s")
|
||||
|
||||
@@ -156,33 +157,52 @@ def get_data_with_opening_closing(filters, account_details, gl_entries):
|
||||
if filters.get("account") or filters.get("party"):
|
||||
data += [get_balance_row(_("Opening"), opening, opening_in_account_currency), {}]
|
||||
|
||||
for acc, acc_dict in gle_map.items():
|
||||
if acc_dict.entries:
|
||||
# Opening for individual ledger, if grouped by account
|
||||
if filters.get("group_by_account"):
|
||||
data.append(get_balance_row(_("Opening"), acc_dict.opening,
|
||||
acc_dict.opening_in_account_currency))
|
||||
if filters.get("group_by_account"):
|
||||
for acc, acc_dict in gle_map.items():
|
||||
if acc_dict.entries:
|
||||
# Opening for individual ledger, if grouped by account
|
||||
if filters.get("group_by_account"):
|
||||
data.append(get_balance_row(_("Opening"), acc_dict.opening,
|
||||
acc_dict.opening_in_account_currency))
|
||||
|
||||
data += acc_dict.entries
|
||||
data += acc_dict.entries
|
||||
|
||||
# Totals and closing for individual ledger, if grouped by account
|
||||
if filters.get("group_by_account"):
|
||||
account_closing = acc_dict.opening + acc_dict.total_debit - acc_dict.total_credit
|
||||
account_closing_in_account_currency = acc_dict.opening_in_account_currency \
|
||||
+ acc_dict.total_debit_in_account_currency - acc_dict.total_credit_in_account_currency
|
||||
|
||||
data += [{"account": "'" + _("Totals") + "'", "debit": acc_dict.total_debit,
|
||||
"credit": acc_dict.total_credit},
|
||||
get_balance_row(_("Closing (Opening + Totals)"),
|
||||
account_closing, account_closing_in_account_currency), {}]
|
||||
# Totals and closing for individual ledger, if grouped by account
|
||||
if filters.get("group_by_account"):
|
||||
account_closing = acc_dict.opening + acc_dict.total_debit - acc_dict.total_credit
|
||||
account_closing_in_account_currency = acc_dict.opening_in_account_currency \
|
||||
+ acc_dict.total_debit_in_account_currency - acc_dict.total_credit_in_account_currency
|
||||
|
||||
data += [{"account": "'" + _("Totals") + "'", "debit": acc_dict.total_debit,
|
||||
"credit": acc_dict.total_credit},
|
||||
get_balance_row(_("Closing (Opening + Totals)"),
|
||||
account_closing, account_closing_in_account_currency), {}]
|
||||
|
||||
else:
|
||||
from_date, to_date = getdate(filters.from_date), getdate(filters.to_date)
|
||||
opening_debit = opening_credit = 0.0
|
||||
|
||||
for gl in gl_entries:
|
||||
if gl.posting_date < from_date:
|
||||
opening_debit += flt(gl.debit, 3)
|
||||
opening_credit += flt(gl.credit, 3)
|
||||
else:
|
||||
data.append(gl)
|
||||
|
||||
if not (filters.get("account") or filters.get("party")):
|
||||
data = [{
|
||||
"account": "'" + _("Opening") + "'",
|
||||
"debit": opening_debit,
|
||||
"credit": opening_credit
|
||||
}] + data
|
||||
|
||||
# Total debit and credit between from and to date
|
||||
if total_debit or total_credit:
|
||||
data.append({
|
||||
"account": "'" + _("Totals") + "'",
|
||||
"debit": total_debit,
|
||||
"account": "'" + _("Totals") + "'",
|
||||
"debit": total_debit,
|
||||
"credit": total_credit,
|
||||
"debit_in_account_currency": total_debit_in_account_currency,
|
||||
"debit_in_account_currency": total_debit_in_account_currency,
|
||||
"credit_in_account_currency": total_credit_in_account_currency
|
||||
})
|
||||
|
||||
@@ -191,7 +211,7 @@ def get_data_with_opening_closing(filters, account_details, gl_entries):
|
||||
closing = opening + total_debit - total_credit
|
||||
closing_in_account_currency = opening_in_account_currency + \
|
||||
total_debit_in_account_currency - total_credit_in_account_currency
|
||||
|
||||
|
||||
data.append(get_balance_row(_("Closing (Opening + Totals)"),
|
||||
closing, closing_in_account_currency))
|
||||
|
||||
@@ -216,38 +236,38 @@ def initialize_gle_map(gl_entries):
|
||||
def get_accountwise_gle(filters, gl_entries, gle_map):
|
||||
opening, total_debit, total_credit = 0, 0, 0
|
||||
opening_in_account_currency, total_debit_in_account_currency, total_credit_in_account_currency = 0, 0, 0
|
||||
|
||||
|
||||
from_date, to_date = getdate(filters.from_date), getdate(filters.to_date)
|
||||
for gle in gl_entries:
|
||||
amount = flt(gle.debit, 3) - flt(gle.credit, 3)
|
||||
amount_in_account_currency = flt(gle.debit_in_account_currency, 3) - flt(gle.credit_in_account_currency, 3)
|
||||
|
||||
|
||||
if (filters.get("account") or filters.get("party") or filters.get("group_by_account")) \
|
||||
and (gle.posting_date < from_date or cstr(gle.is_opening) == "Yes"):
|
||||
|
||||
|
||||
gle_map[gle.account].opening += amount
|
||||
if filters.get("show_in_account_currency"):
|
||||
gle_map[gle.account].opening_in_account_currency += amount_in_account_currency
|
||||
|
||||
|
||||
if filters.get("account") or filters.get("party"):
|
||||
opening += amount
|
||||
if filters.get("show_in_account_currency"):
|
||||
opening_in_account_currency += amount_in_account_currency
|
||||
|
||||
|
||||
elif gle.posting_date <= to_date:
|
||||
gle_map[gle.account].entries.append(gle)
|
||||
gle_map[gle.account].total_debit += flt(gle.debit, 3)
|
||||
gle_map[gle.account].total_credit += flt(gle.credit, 3)
|
||||
|
||||
|
||||
total_debit += flt(gle.debit, 3)
|
||||
total_credit += flt(gle.credit, 3)
|
||||
|
||||
|
||||
if filters.get("show_in_account_currency"):
|
||||
gle_map[gle.account].total_debit_in_account_currency += flt(gle.debit_in_account_currency, 3)
|
||||
gle_map[gle.account].total_credit_in_account_currency += flt(gle.credit_in_account_currency, 3)
|
||||
|
||||
|
||||
total_debit_in_account_currency += flt(gle.debit_in_account_currency, 3)
|
||||
total_credit_in_account_currency += flt(gle.credit_in_account_currency, 3)
|
||||
total_credit_in_account_currency += flt(gle.credit_in_account_currency, 3)
|
||||
|
||||
return opening, total_debit, total_credit, opening_in_account_currency, \
|
||||
total_debit_in_account_currency, total_credit_in_account_currency, gle_map
|
||||
@@ -258,27 +278,27 @@ def get_balance_row(label, balance, balance_in_account_currency=None):
|
||||
"debit": balance if balance > 0 else 0,
|
||||
"credit": -1*balance if balance < 0 else 0
|
||||
}
|
||||
|
||||
|
||||
if balance_in_account_currency != None:
|
||||
balance_row.update({
|
||||
"debit_in_account_currency": balance_in_account_currency if balance_in_account_currency > 0 else 0,
|
||||
"credit_in_account_currency": -1*balance_in_account_currency if balance_in_account_currency < 0 else 0
|
||||
})
|
||||
|
||||
|
||||
return balance_row
|
||||
|
||||
def get_result_as_list(data, filters):
|
||||
result = []
|
||||
for d in data:
|
||||
row = [d.get("posting_date"), d.get("account"), d.get("debit"), d.get("credit")]
|
||||
|
||||
|
||||
if filters.get("show_in_account_currency"):
|
||||
row += [d.get("debit_in_account_currency"), d.get("credit_in_account_currency")]
|
||||
|
||||
row += [d.get("voucher_type"), d.get("voucher_no"), d.get("against"),
|
||||
|
||||
row += [d.get("voucher_type"), d.get("voucher_no"), d.get("against"),
|
||||
d.get("party_type"), d.get("party"), d.get("cost_center"), d.get("remarks")
|
||||
]
|
||||
|
||||
|
||||
result.append(row)
|
||||
|
||||
return result
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.query_reports["Trial Balance for Party"] = {
|
||||
"filters": [
|
||||
{
|
||||
"fieldname": "company",
|
||||
"label": __("Company"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Company",
|
||||
"default": frappe.defaults.get_user_default("company"),
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "fiscal_year",
|
||||
"label": __("Fiscal Year"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Fiscal Year",
|
||||
"default": frappe.defaults.get_user_default("fiscal_year"),
|
||||
"reqd": 1,
|
||||
"on_change": function(query_report) {
|
||||
var fiscal_year = query_report.get_values().fiscal_year;
|
||||
if (!fiscal_year) {
|
||||
return;
|
||||
}
|
||||
frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) {
|
||||
var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);
|
||||
query_report.filters_by_name.from_date.set_input(fy.year_start_date);
|
||||
query_report.filters_by_name.to_date.set_input(fy.year_end_date);
|
||||
query_report.trigger_refresh();
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "from_date",
|
||||
"label": __("From Date"),
|
||||
"fieldtype": "Date",
|
||||
"default": frappe.defaults.get_user_default("year_start_date"),
|
||||
},
|
||||
{
|
||||
"fieldname": "to_date",
|
||||
"label": __("To Date"),
|
||||
"fieldtype": "Date",
|
||||
"default": frappe.defaults.get_user_default("year_end_date"),
|
||||
},
|
||||
{
|
||||
"fieldname":"party_type",
|
||||
"label": __("Party Type"),
|
||||
"fieldtype": "Select",
|
||||
"options": ["Customer", "Supplier"],
|
||||
"default": "Customer"
|
||||
},
|
||||
{
|
||||
"fieldname": "show_zero_values",
|
||||
"label": __("Show zero values"),
|
||||
"fieldtype": "Check"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2015-09-22 10:28:45.762272",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"is_standard": "Yes",
|
||||
"modified": "2015-09-22 10:28:45.762272",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Trial Balance for Party",
|
||||
"owner": "Administrator",
|
||||
"ref_doctype": "GL Entry",
|
||||
"report_name": "Trial Balance for Party",
|
||||
"report_type": "Script Report"
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import flt, cint
|
||||
from erpnext.accounts.report.trial_balance.trial_balance import validate_filters
|
||||
|
||||
|
||||
def execute(filters=None):
|
||||
validate_filters(filters)
|
||||
|
||||
show_party_name = is_party_name_visible(filters)
|
||||
|
||||
columns = get_columns(filters, show_party_name)
|
||||
data = get_data(filters, show_party_name)
|
||||
|
||||
return columns, data
|
||||
|
||||
def get_data(filters, show_party_name):
|
||||
party_name_field = "customer_name" if filters.get("party_type")=="Customer" else "supplier_name"
|
||||
parties = frappe.get_all(filters.get("party_type"), fields = ["name", party_name_field], order_by="name")
|
||||
|
||||
opening_balances = get_opening_balances(filters)
|
||||
balances_within_period = get_balances_within_period(filters)
|
||||
|
||||
data = []
|
||||
total_debit, total_credit = 0, 0
|
||||
for party in parties:
|
||||
row = { "party": party.name }
|
||||
if show_party_name:
|
||||
row["party_name"] = party.get(party_name_field)
|
||||
|
||||
# opening
|
||||
opening_debit, opening_credit = opening_balances.get(party.name, [0, 0])
|
||||
row.update({
|
||||
"opening_debit": opening_debit,
|
||||
"opening_credit": opening_credit
|
||||
})
|
||||
|
||||
# within period
|
||||
debit, credit = balances_within_period.get(party.name, [0, 0])
|
||||
row.update({
|
||||
"debit": debit,
|
||||
"credit": credit
|
||||
})
|
||||
|
||||
# totals
|
||||
total_debit += debit
|
||||
total_credit += credit
|
||||
|
||||
# closing
|
||||
closing_debit, closing_credit = toggle_debit_credit(opening_debit + debit, opening_credit + credit)
|
||||
row.update({
|
||||
"closing_debit": closing_debit,
|
||||
"closing_credit": closing_credit
|
||||
})
|
||||
|
||||
has_value = False
|
||||
if (opening_debit or opening_credit or debit or credit or closing_debit or closing_credit):
|
||||
has_value =True
|
||||
|
||||
if cint(filters.show_zero_values) or has_value:
|
||||
data.append(row)
|
||||
|
||||
# Add total row
|
||||
if total_debit or total_credit:
|
||||
data.append({
|
||||
"party": "'" + _("Totals") + "'",
|
||||
"debit": total_debit,
|
||||
"credit": total_credit
|
||||
})
|
||||
|
||||
return data
|
||||
|
||||
def get_opening_balances(filters):
|
||||
gle = frappe.db.sql("""
|
||||
select party, sum(ifnull(debit, 0)) as opening_debit, sum(ifnull(credit, 0)) as opening_credit
|
||||
from `tabGL Entry`
|
||||
where company=%(company)s
|
||||
and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != ''
|
||||
and (posting_date < %(from_date)s or ifnull(is_opening, 'No') = 'Yes')
|
||||
group by party""", {
|
||||
"company": filters.company,
|
||||
"from_date": filters.from_date,
|
||||
"party_type": filters.party_type
|
||||
}, as_dict=True)
|
||||
|
||||
opening = frappe._dict()
|
||||
for d in gle:
|
||||
opening_debit, opening_credit = toggle_debit_credit(d.opening_debit, d.opening_credit)
|
||||
opening.setdefault(d.party, [opening_debit, opening_credit])
|
||||
|
||||
return opening
|
||||
|
||||
def get_balances_within_period(filters):
|
||||
gle = frappe.db.sql("""
|
||||
select party, sum(ifnull(debit, 0)) as debit, sum(ifnull(credit, 0)) as credit
|
||||
from `tabGL Entry`
|
||||
where company=%(company)s
|
||||
and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != ''
|
||||
and posting_date >= %(from_date)s and posting_date <= %(to_date)s
|
||||
and ifnull(is_opening, 'No') = 'No'
|
||||
group by party""", {
|
||||
"company": filters.company,
|
||||
"from_date": filters.from_date,
|
||||
"to_date": filters.to_date,
|
||||
"party_type": filters.party_type
|
||||
}, as_dict=True)
|
||||
|
||||
balances_within_period = frappe._dict()
|
||||
for d in gle:
|
||||
balances_within_period.setdefault(d.party, [d.debit, d.credit])
|
||||
|
||||
return balances_within_period
|
||||
|
||||
def toggle_debit_credit(debit, credit):
|
||||
if flt(debit) > flt(credit):
|
||||
debit = flt(debit) - flt(credit)
|
||||
credit = 0.0
|
||||
else:
|
||||
credit = flt(credit) - flt(debit)
|
||||
debit = 0.0
|
||||
|
||||
return debit, credit
|
||||
|
||||
def get_columns(filters, show_party_name):
|
||||
columns = [
|
||||
{
|
||||
"fieldname": "party",
|
||||
"label": _(filters.party_type),
|
||||
"fieldtype": "Link",
|
||||
"options": filters.party_type,
|
||||
"width": 200
|
||||
},
|
||||
{
|
||||
"fieldname": "opening_debit",
|
||||
"label": _("Opening (Dr)"),
|
||||
"fieldtype": "Currency",
|
||||
"width": 120
|
||||
},
|
||||
{
|
||||
"fieldname": "opening_credit",
|
||||
"label": _("Opening (Cr)"),
|
||||
"fieldtype": "Currency",
|
||||
"width": 120
|
||||
},
|
||||
{
|
||||
"fieldname": "debit",
|
||||
"label": _("Debit"),
|
||||
"fieldtype": "Currency",
|
||||
"width": 120
|
||||
},
|
||||
{
|
||||
"fieldname": "credit",
|
||||
"label": _("Credit"),
|
||||
"fieldtype": "Currency",
|
||||
"width": 120
|
||||
},
|
||||
{
|
||||
"fieldname": "closing_debit",
|
||||
"label": _("Closing (Dr)"),
|
||||
"fieldtype": "Currency",
|
||||
"width": 120
|
||||
},
|
||||
{
|
||||
"fieldname": "closing_credit",
|
||||
"label": _("Closing (Cr)"),
|
||||
"fieldtype": "Currency",
|
||||
"width": 120
|
||||
}
|
||||
]
|
||||
|
||||
if show_party_name:
|
||||
columns.insert(1, {
|
||||
"fieldname": "party_name",
|
||||
"label": _(filters.party_type) + " Name",
|
||||
"fieldtype": "Data",
|
||||
"width": 200
|
||||
})
|
||||
|
||||
return columns
|
||||
|
||||
def is_party_name_visible(filters):
|
||||
show_party_name = False
|
||||
if filters.get("party_type") == "Customer":
|
||||
party_naming_by = frappe.db.get_single_value("Selling Settings", "cust_master_name")
|
||||
else:
|
||||
party_naming_by = frappe.db.get_single_value("Buying Settings", "supp_master_name")
|
||||
|
||||
if party_naming_by == "Naming Series":
|
||||
show_party_name = True
|
||||
|
||||
return show_party_name
|
||||
@@ -9,6 +9,9 @@ from frappe import throw, _
|
||||
from frappe.utils import formatdate
|
||||
import frappe.desk.reportview
|
||||
|
||||
# imported to enable erpnext.accounts.utils.get_account_currency
|
||||
from erpnext.accounts.doctype.account.account import get_account_currency
|
||||
|
||||
class FiscalYearError(frappe.ValidationError): pass
|
||||
class BudgetError(frappe.ValidationError): pass
|
||||
|
||||
@@ -94,8 +97,8 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, in_acco
|
||||
select name from `tabAccount` ac where ac.name = gle.account
|
||||
and ac.lft >= %s and ac.rgt <= %s
|
||||
)""" % (acc.lft, acc.rgt))
|
||||
|
||||
# If group and currency same as company,
|
||||
|
||||
# If group and currency same as company,
|
||||
# always return balance based on debit and credit in company currency
|
||||
if acc.account_currency == frappe.db.get_value("Company", acc.company, "default_currency"):
|
||||
in_account_currency = False
|
||||
@@ -397,7 +400,7 @@ def get_stock_rbnb_difference(posting_date, company):
|
||||
# Amount should be credited
|
||||
return flt(stock_rbnb) + flt(sys_bal)
|
||||
|
||||
def get_outstanding_invoices(amount_query, account, party_type, party, with_journal_entry=True):
|
||||
def get_outstanding_invoices(amount_query, account, party_type, party):
|
||||
all_outstanding_vouchers = []
|
||||
outstanding_voucher_list = frappe.db.sql("""
|
||||
select
|
||||
@@ -407,6 +410,11 @@ def get_outstanding_invoices(amount_query, account, party_type, party, with_jour
|
||||
`tabGL Entry`
|
||||
where
|
||||
account = %s and party_type=%s and party=%s and {amount_query} > 0
|
||||
and (CASE
|
||||
WHEN voucher_type = 'Journal Entry'
|
||||
THEN ifnull(against_voucher, '') = ''
|
||||
ELSE 1=1
|
||||
END)
|
||||
group by voucher_type, voucher_no
|
||||
""".format(amount_query = amount_query), (account, party_type, party), as_dict = True)
|
||||
|
||||
@@ -425,9 +433,6 @@ def get_outstanding_invoices(amount_query, account, party_type, party, with_jour
|
||||
payment_amount = -1*payment_amount[0][0] if payment_amount else 0
|
||||
precision = frappe.get_precision("Sales Invoice", "outstanding_amount")
|
||||
|
||||
if not with_journal_entry and d.voucher_type=="Journal Entry":
|
||||
continue
|
||||
|
||||
if d.invoice_amount > payment_amount:
|
||||
|
||||
all_outstanding_vouchers.append({
|
||||
|
||||
@@ -4,17 +4,17 @@ frappe.listview_settings['Purchase Order'] = {
|
||||
get_indicator: function(doc) {
|
||||
if(doc.status==="Stopped") {
|
||||
return [__("Stopped"), "darkgrey", "status,=,Stopped"];
|
||||
} else if(flt(doc.per_received) < 100 && doc.status!=="Stopped") {
|
||||
if(flt(doc.per_billed) < 100) {
|
||||
} else if(flt(doc.per_received, 2) < 100 && doc.status!=="Stopped") {
|
||||
if(flt(doc.per_billed, 2) < 100) {
|
||||
return [__("To Receive and Bill"), "orange",
|
||||
"per_received,<,100|per_billed,<,100|status,!=,Stopped"];
|
||||
} else {
|
||||
return [__("To Receive"), "orange",
|
||||
"per_received,<,100|per_billed,=,100|status,!=,Stopped"];
|
||||
}
|
||||
} else if(flt(doc.per_received) == 100 && flt(doc.per_billed) < 100 && doc.status!=="Stopped") {
|
||||
} else if(flt(doc.per_received, 2) == 100 && flt(doc.per_billed, 2) < 100 && doc.status!=="Stopped") {
|
||||
return [__("To Bill"), "orange", "per_received,=,100|per_billed,<,100|status,!=,Stopped"];
|
||||
} else if(flt(doc.per_received) == 100 && flt(doc.per_billed) == 100 && doc.status!=="Stopped") {
|
||||
} else if(flt(doc.per_received, 2) == 100 && flt(doc.per_billed, 2) == 100 && doc.status!=="Stopped") {
|
||||
return [__("Completed"), "green", "per_received,=,100|per_billed,=,100|status,!=,Stopped"];
|
||||
}
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ from frappe import msgprint, _
|
||||
from frappe.model.naming import make_autoname
|
||||
from erpnext.utilities.address_and_contact import load_address_and_contact
|
||||
from erpnext.utilities.transaction_base import TransactionBase
|
||||
from erpnext.accounts.party import validate_accounting_currency, validate_party_account
|
||||
from erpnext.accounts.party import validate_party_accounts
|
||||
|
||||
class Supplier(TransactionBase):
|
||||
def get_feed(self):
|
||||
@@ -45,9 +45,8 @@ class Supplier(TransactionBase):
|
||||
if frappe.defaults.get_global_default('supp_master_name') == 'Naming Series':
|
||||
if not self.naming_series:
|
||||
msgprint(_("Series is mandatory"), raise_exception=1)
|
||||
|
||||
validate_accounting_currency(self)
|
||||
validate_party_account(self)
|
||||
|
||||
validate_party_accounts(self)
|
||||
|
||||
def get_contacts(self,nm):
|
||||
if nm:
|
||||
@@ -96,14 +95,14 @@ def get_dashboard_info(supplier):
|
||||
billing_this_year = frappe.db.sql("""
|
||||
select sum(ifnull(credit_in_account_currency, 0)) - sum(ifnull(debit_in_account_currency, 0))
|
||||
from `tabGL Entry`
|
||||
where voucher_type='Purchase Invoice' and party_type = 'Supplier'
|
||||
and party=%s and fiscal_year = %s""",
|
||||
where voucher_type='Purchase Invoice' and party_type = 'Supplier'
|
||||
and party=%s and fiscal_year = %s""",
|
||||
(supplier, frappe.db.get_default("fiscal_year")))
|
||||
|
||||
|
||||
total_unpaid = frappe.db.sql("""select sum(outstanding_amount)
|
||||
from `tabPurchase Invoice`
|
||||
where supplier=%s and docstatus = 1""", supplier)
|
||||
|
||||
|
||||
|
||||
out["billing_this_year"] = billing_this_year[0][0] if billing_this_year else 0
|
||||
out["total_unpaid"] = total_unpaid[0][0] if total_unpaid else 0
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
[
|
||||
{
|
||||
"doctype": "Supplier",
|
||||
"supplier_name": "_Test Supplier",
|
||||
"doctype": "Supplier",
|
||||
"supplier_name": "_Test Supplier",
|
||||
"supplier_type": "_Test Supplier Type"
|
||||
},
|
||||
{
|
||||
"doctype": "Supplier",
|
||||
"supplier_name": "_Test Supplier 1",
|
||||
"doctype": "Supplier",
|
||||
"supplier_name": "_Test Supplier 1",
|
||||
"supplier_type": "_Test Supplier Type"
|
||||
},
|
||||
{
|
||||
"doctype": "Supplier",
|
||||
"supplier_name": "_Test Supplier USD",
|
||||
"doctype": "Supplier",
|
||||
"supplier_name": "_Test Supplier USD",
|
||||
"supplier_type": "_Test Supplier Type",
|
||||
"party_account_currency": "USD",
|
||||
"accounts": [{
|
||||
"company": "_Test Company",
|
||||
"account": "_Test Payable USD - _TC"
|
||||
}]
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
8
erpnext/change_log/v6/v6_3_0.md
Normal file
8
erpnext/change_log/v6/v6_3_0.md
Normal file
@@ -0,0 +1,8 @@
|
||||
- **Tax Rule:** Define rules to automatically select a Tax Template based on Customer, Supplier, Billing Address or Shipping Address
|
||||
- Changes to **Shopping Cart:**
|
||||
- The prices will be based on only a single Price List defined in Shopping Cart Settings. Essentially, it means that your Shopping Cart will be available only in a single currency.
|
||||
- Shipping Rule will be defined per Country, instead of Territory.
|
||||
- Taxes will be applied based on the new Tax Rule system, instead of Territory.
|
||||
- **Important Note:** Your Shopping Cart Settings have been disabled. The new changes require you to review your Price List, Tax Rules and Shipping Rule, update the settings, and then enable Shopping Cart again.
|
||||
- Enhancements in Customer Portal user interface
|
||||
- Sales Team is now editable after submission of Sales Order, Sales Invoice and Delivery Note
|
||||
4
erpnext/change_log/v6/v6_4_0.md
Normal file
4
erpnext/change_log/v6/v6_4_0.md
Normal file
@@ -0,0 +1,4 @@
|
||||
- Trial Balance for Customer and Supplier
|
||||
- Chart of Accounts for Guatemala
|
||||
- Address and Contact permissions based on Customer and Supplier
|
||||
- Multi-currency Accounting: Allow Accounts in different currencies for a Customer or Supplier, if they belong to a different Company
|
||||
@@ -106,6 +106,11 @@ def get_data():
|
||||
"name": "Accounts Settings",
|
||||
"description": _("Default settings for accounting transactions.")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Tax Rule",
|
||||
"description": _("Tax Rule for transactions.")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Sales Taxes and Charges Template",
|
||||
@@ -182,6 +187,12 @@ def get_data():
|
||||
"doctype": "GL Entry",
|
||||
"is_query_report": True,
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "Trial Balance for Party",
|
||||
"doctype": "GL Entry",
|
||||
"is_query_report": True,
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"name": "Gross Profit",
|
||||
|
||||
@@ -6,16 +6,15 @@ import frappe
|
||||
from frappe import _, throw
|
||||
from frappe.utils import today, flt, cint
|
||||
from erpnext.setup.utils import get_company_currency, get_exchange_rate
|
||||
from erpnext.accounts.utils import get_fiscal_year, validate_fiscal_year
|
||||
from erpnext.accounts.utils import get_fiscal_year, validate_fiscal_year, get_account_currency
|
||||
from erpnext.utilities.transaction_base import TransactionBase
|
||||
from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document
|
||||
from erpnext.controllers.sales_and_purchase_return import validate_return
|
||||
from erpnext.accounts.party import get_party_account_currency, validate_party_gle_currency
|
||||
from erpnext.exceptions import CustomerFrozen, InvalidCurrency
|
||||
|
||||
force_item_fields = ("item_group", "barcode", "brand", "stock_uom")
|
||||
|
||||
class CustomerFrozen(frappe.ValidationError): pass
|
||||
class InvalidCurrency(frappe.ValidationError): pass
|
||||
|
||||
class AccountsController(TransactionBase):
|
||||
def __init__(self, arg1, arg2=None):
|
||||
super(AccountsController, self).__init__(arg1, arg2)
|
||||
@@ -167,6 +166,9 @@ class AccountsController(TransactionBase):
|
||||
if ret.get("pricing_rule_for") == "Price":
|
||||
item.set("pricing_list_rate", ret.get("pricing_list_rate"))
|
||||
|
||||
if item.price_list_rate:
|
||||
item.rate = flt(item.price_list_rate *
|
||||
(1.0 - (flt(item.discount_percentage) / 100.0)), item.precision("rate"))
|
||||
|
||||
def set_taxes(self):
|
||||
if not self.meta.get_field("taxes"):
|
||||
@@ -217,7 +219,7 @@ class AccountsController(TransactionBase):
|
||||
gl_dict.update(args)
|
||||
|
||||
if not account_currency:
|
||||
account_currency = frappe.db.get_value("Account", gl_dict.account, "account_currency")
|
||||
account_currency = get_account_currency(gl_dict.account)
|
||||
|
||||
if self.doctype != "Journal Entry":
|
||||
self.validate_account_currency(gl_dict.account, account_currency)
|
||||
@@ -424,8 +426,7 @@ class AccountsController(TransactionBase):
|
||||
if self.get("currency"):
|
||||
party_type, party = self.get_party()
|
||||
if party_type and party:
|
||||
party_account_currency = frappe.db.get_value(party_type, party, "party_account_currency") \
|
||||
or self.company_currency
|
||||
party_account_currency = get_party_account_currency(party_type, party, self.company)
|
||||
|
||||
if party_account_currency != self.company_currency and self.currency != party_account_currency:
|
||||
frappe.throw(_("Accounting Entry for {0}: {1} can only be made in currency: {2}")
|
||||
|
||||
@@ -207,10 +207,10 @@ class StatusUpdater(Document):
|
||||
# update percent complete in the parent table
|
||||
if args.get('target_parent_field'):
|
||||
frappe.db.sql("""update `tab%(target_parent_dt)s`
|
||||
set %(target_parent_field)s = (select sum(if(%(target_ref_field)s >
|
||||
set %(target_parent_field)s = round((select sum(if(%(target_ref_field)s >
|
||||
ifnull(%(target_field)s, 0), %(target_field)s,
|
||||
%(target_ref_field)s))/sum(%(target_ref_field)s)*100
|
||||
from `tab%(target_dt)s` where parent="%(name)s") %(set_modified)s
|
||||
from `tab%(target_dt)s` where parent="%(name)s"), 2) %(set_modified)s
|
||||
where name='%(name)s'""" % args)
|
||||
|
||||
# update field
|
||||
|
||||
@@ -21,37 +21,57 @@ def get_list_context(context=None):
|
||||
def get_transaction_list(doctype, txt=None, filters=None, limit_start=0, limit_page_length=20):
|
||||
from frappe.templates.pages.list import get_list
|
||||
user = frappe.session.user
|
||||
key = None
|
||||
|
||||
if not filters: filters = []
|
||||
|
||||
filters.append((doctype, "docstatus", "=", 1))
|
||||
|
||||
if user != "Guest" and is_website_user():
|
||||
# find party for this contact
|
||||
customers, suppliers = get_customers_suppliers(doctype, user)
|
||||
|
||||
if customers:
|
||||
return post_process(get_list(doctype, txt, filters=[(doctype, "customer", "in", customers)],
|
||||
limit_start=limit_start, limit_page_length=limit_page_length, ignore_permissions=True))
|
||||
|
||||
key, parties = "customer", customers
|
||||
elif suppliers:
|
||||
return post_process(get_list(doctype, txt, filters=[(doctype, "supplier", "in", suppliers)],
|
||||
limit_start=limit_start, limit_page_length=limit_page_length, ignore_permissions=True))
|
||||
key, parties = "supplier", suppliers
|
||||
|
||||
filters.append((doctype, key, "in", parties))
|
||||
|
||||
if key:
|
||||
return post_process(doctype, get_list(doctype, txt,
|
||||
filters=filters, fields = "name",
|
||||
limit_start=limit_start, limit_page_length=limit_page_length,
|
||||
ignore_permissions=True,
|
||||
order_by = "modified desc"))
|
||||
else:
|
||||
return []
|
||||
|
||||
return post_process(get_list(doctype, txt, filters, limit_start, limit_page_length))
|
||||
return post_process(doctype, get_list(doctype, txt, filters, limit_start, limit_page_length,
|
||||
fields="name", order_by = "modified desc"))
|
||||
|
||||
def post_process(result):
|
||||
for r in result:
|
||||
r.status_percent = 0
|
||||
r.status_display = []
|
||||
def post_process(doctype, data):
|
||||
result = []
|
||||
for d in data:
|
||||
doc = frappe.get_doc(doctype, d.name)
|
||||
|
||||
if r.get("per_billed"):
|
||||
r.status_percent += flt(r.per_billed)
|
||||
r.status_display.append(_("Billed") if r.per_billed==100 else _("{0}% Billed").format(r.per_billed))
|
||||
doc.status_percent = 0
|
||||
doc.status_display = []
|
||||
|
||||
if r.get("per_delivered"):
|
||||
r.status_percent += flt(r.per_delivered)
|
||||
r.status_display.append(_("Delivered") if r.per_delivered==100 else _("{0}% Delivered").format(r.per_delivered))
|
||||
if doc.get("per_billed"):
|
||||
doc.status_percent += flt(doc.per_billed)
|
||||
doc.status_display.append(_("Billed") if doc.per_billed==100 else _("{0}% Billed").format(doc.per_billed))
|
||||
|
||||
r.status_display = ", ".join(r.status_display)
|
||||
if doc.get("per_delivered"):
|
||||
doc.status_percent += flt(doc.per_delivered)
|
||||
doc.status_display.append(_("Delivered") if doc.per_delivered==100 else _("{0}% Delivered").format(doc.per_delivered))
|
||||
|
||||
if hasattr(doc, "set_indicator"):
|
||||
doc.set_indicator()
|
||||
|
||||
doc.status_display = ", ".join(doc.status_display)
|
||||
doc.items_preview = ", ".join([d.item_name for d in doc.items])
|
||||
result.append(doc)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@@ -62,7 +62,8 @@ class Newsletter(Document):
|
||||
subject = self.subject, message = self.message,
|
||||
reference_doctype = self.doctype, reference_name = self.name,
|
||||
unsubscribe_method = "/api/method/erpnext.crm.doctype.newsletter.newsletter.unsubscribe",
|
||||
unsubscribe_params = {"name": self.newsletter_list})
|
||||
unsubscribe_params = {"name": self.newsletter_list},
|
||||
bulk_priority = 1)
|
||||
|
||||
if not frappe.flags.in_test:
|
||||
frappe.db.auto_commit_on_many_writes = False
|
||||
|
||||
@@ -7,8 +7,8 @@ from frappe.utils import cstr, cint
|
||||
from frappe import msgprint, _
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
from erpnext.setup.utils import get_exchange_rate
|
||||
|
||||
from erpnext.utilities.transaction_base import TransactionBase
|
||||
from erpnext.accounts.party import get_party_account_currency
|
||||
|
||||
subject_field = "title"
|
||||
sender_field = "contact_email"
|
||||
@@ -180,9 +180,10 @@ def get_item_details(item_code):
|
||||
def make_quotation(source_name, target_doc=None):
|
||||
def set_missing_values(source, target):
|
||||
quotation = frappe.get_doc(target)
|
||||
|
||||
|
||||
company_currency = frappe.db.get_value("Company", quotation.company, "default_currency")
|
||||
party_account_currency = frappe.db.get_value("Customer", quotation.customer, "party_account_currency")
|
||||
party_account_currency = get_party_account_currency("Customer", quotation.customer, quotation.company)
|
||||
|
||||
if company_currency == party_account_currency:
|
||||
exchange_rate = 1
|
||||
else:
|
||||
@@ -190,7 +191,7 @@ def make_quotation(source_name, target_doc=None):
|
||||
|
||||
quotation.currency = party_account_currency or company_currency
|
||||
quotation.conversion_rate = exchange_rate
|
||||
|
||||
|
||||
quotation.run_method("set_missing_values")
|
||||
quotation.run_method("calculate_taxes_and_totals")
|
||||
|
||||
|
||||
7
erpnext/exceptions.py
Normal file
7
erpnext/exceptions.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
# accounts
|
||||
class CustomerFrozen(frappe.ValidationError): pass
|
||||
class InvalidAccountCurrency(frappe.ValidationError): pass
|
||||
class InvalidCurrency(frappe.ValidationError): pass
|
||||
@@ -1,4 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
from frappe import _
|
||||
|
||||
app_name = "erpnext"
|
||||
app_title = "ERPNext"
|
||||
app_publisher = "Frappe Technologies Pvt. Ltd."
|
||||
@@ -27,7 +29,7 @@ blogs.
|
||||
"""
|
||||
app_icon = "icon-th"
|
||||
app_color = "#e74c3c"
|
||||
app_version = "6.2.0"
|
||||
app_version = "6.4.0"
|
||||
github_link = "https://github.com/frappe/erpnext"
|
||||
|
||||
error_report_email = "support@erpnext.com"
|
||||
@@ -62,11 +64,26 @@ website_context = {
|
||||
|
||||
website_route_rules = [
|
||||
{"from_route": "/orders", "to_route": "Sales Order"},
|
||||
{"from_route": "/orders/<path:name>", "to_route": "print", "defaults": {"doctype": "Sales Order"}},
|
||||
{"from_route": "/orders/<path:name>", "to_route": "order",
|
||||
"defaults": {
|
||||
"doctype": "Sales Order",
|
||||
"parents": [{"title": _("Orders"), "name": "orders"}]
|
||||
}
|
||||
},
|
||||
{"from_route": "/invoices", "to_route": "Sales Invoice"},
|
||||
{"from_route": "/invoices/<path:name>", "to_route": "print", "defaults": {"doctype": "Sales Invoice"}},
|
||||
{"from_route": "/invoices/<path:name>", "to_route": "order",
|
||||
"defaults": {
|
||||
"doctype": "Sales Invoice",
|
||||
"parents": [{"title": _("Invoices"), "name": "invoices"}]
|
||||
}
|
||||
},
|
||||
{"from_route": "/shipments", "to_route": "Delivery Note"},
|
||||
{"from_route": "/shipments/<path:name>", "to_route": "print", "defaults": {"doctype": "Delivery Note"}}
|
||||
{"from_route": "/shipments/<path:name>", "to_route": "order",
|
||||
"defaults": {
|
||||
"doctype": "Delivery Notes",
|
||||
"parents": [{"title": _("Shipments"), "name": "shipments"}]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
has_website_permission = {
|
||||
@@ -76,6 +93,16 @@ has_website_permission = {
|
||||
"Issue": "erpnext.support.doctype.issue.issue.has_website_permission"
|
||||
}
|
||||
|
||||
permission_query_conditions = {
|
||||
"Contact": "erpnext.utilities.address_and_contact.get_permission_query_conditions_for_contact",
|
||||
"Address": "erpnext.utilities.address_and_contact.get_permission_query_conditions_for_address"
|
||||
}
|
||||
|
||||
has_permission = {
|
||||
"Contact": "erpnext.utilities.address_and_contact.has_permission",
|
||||
"Address": "erpnext.utilities.address_and_contact.has_permission"
|
||||
}
|
||||
|
||||
dump_report_map = "erpnext.startup.report_data_map.data_map"
|
||||
|
||||
before_tests = "erpnext.setup.utils.before_tests"
|
||||
@@ -111,12 +138,6 @@ scheduler_events = {
|
||||
"erpnext.support.doctype.issue.issue.auto_close_tickets",
|
||||
"erpnext.accounts.doctype.fiscal_year.fiscal_year.auto_create_fiscal_year",
|
||||
"erpnext.hr.doctype.employee.employee.send_birthday_reminders"
|
||||
],
|
||||
"daily_long": [
|
||||
"erpnext.setup.doctype.backup_manager.backup_manager.take_backups_daily"
|
||||
],
|
||||
"weekly_long": [
|
||||
"erpnext.setup.doctype.backup_manager.backup_manager.take_backups_weekly"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ class Employee(Document):
|
||||
user.user_image = self.image
|
||||
try:
|
||||
frappe.get_doc({
|
||||
"doctype": "File Data",
|
||||
"doctype": "File",
|
||||
"file_name": self.image,
|
||||
"attached_to_doctype": "User",
|
||||
"attached_to_name": self.user_id
|
||||
|
||||
@@ -147,7 +147,7 @@ cur_frm.fields_dict['project_name'].get_query = function(doc, dt, dn) {
|
||||
cur_frm.fields_dict['items'].grid.get_field('item_code').get_query = function(doc) {
|
||||
return{
|
||||
query: "erpnext.controllers.queries.item_query",
|
||||
filters: [["Item", "name", "!=", doc.item]]
|
||||
filters: [["Item", "name", "!=", cur_frm.doc.item]]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -391,8 +391,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1):
|
||||
|
||||
if fetch_exploded:
|
||||
query = query.format(table="BOM Explosion Item",
|
||||
conditions="""and item.is_pro_applicable = 0
|
||||
and item.is_sub_contracted_item = 0 """)
|
||||
conditions="""and item.is_sub_contracted_item = 0""")
|
||||
items = frappe.db.sql(query, { "qty": qty, "bom": bom }, as_dict=True)
|
||||
else:
|
||||
query = query.format(table="BOM Item", conditions="")
|
||||
|
||||
@@ -2,56 +2,13 @@
|
||||
"allow_copy": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:operation",
|
||||
"autoname": "Prompt",
|
||||
"creation": "2014-11-07 16:20:30.683186",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "operation",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Operation",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "column_break_2",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
@@ -61,7 +18,7 @@
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Default Workstation",
|
||||
"no_copy": 0,
|
||||
"options": "Workstation",
|
||||
@@ -127,7 +84,7 @@
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"modified": "2015-09-14 02:55:58.575636",
|
||||
"modified": "2015-09-23 07:32:29.550198",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Operation",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[
|
||||
{
|
||||
"doctype": "Operation",
|
||||
"operation": "_Test Operation 1",
|
||||
"name": "_Test Operation 1",
|
||||
"workstation": "_Test Workstation 1"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -209,3 +209,6 @@ erpnext.patches.v6_0.fix_planned_qty
|
||||
erpnext.patches.v6_0.multi_currency
|
||||
erpnext.patches.v6_2.remove_newsletter_duplicates
|
||||
erpnext.patches.v6_2.fix_missing_default_taxes_and_lead
|
||||
erpnext.patches.v5_8.tax_rule
|
||||
erpnext.patches.v6_3.convert_applicable_territory
|
||||
erpnext.patches.v6_4.round_status_updater_percentages
|
||||
|
||||
@@ -9,7 +9,7 @@ def execute():
|
||||
"Blog Category", "Blog Settings", "Blogger", "Branch", "Brand", "Buying Settings",
|
||||
"Comment", "Communication", "Company", "Contact Us Settings",
|
||||
"Country", "Currency", "Currency Exchange", "Deduction Type", "Department",
|
||||
"Designation", "Earning Type", "Event", "Feed", "File Data", "Fiscal Year",
|
||||
"Designation", "Earning Type", "Event", "Feed", "File", "Fiscal Year",
|
||||
"HR Settings", "Industry Type", "Leave Type", "Letter Head",
|
||||
"Mode of Payment", "Module Def", "Naming Series", "POS Setting", "Print Heading",
|
||||
"Report", "Role", "Selling Settings", "Stock Settings", "Supplier Type", "UOM"):
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
for dt in ("Stock Ledger Entry", "Communication", "Comment", "DefaultValue", "DocShare", "File Data", "ToDo", "Feed"):
|
||||
for dt in ("Stock Ledger Entry", "Communication", "Comment", "DefaultValue", "DocShare", "File", "ToDo", "Feed"):
|
||||
frappe.get_doc("DocType", dt).run_module_method("on_doctype_update")
|
||||
@@ -37,7 +37,7 @@ def fix_files_for_item(files_path, unlinked_files):
|
||||
frappe.db.set_value(row.doctype, row.name, "image", new_file_url, update_modified=False)
|
||||
|
||||
# set it as attachment of this item code
|
||||
file_data = frappe.get_doc("File Data", unlinked_files[file_url]["file"])
|
||||
file_data = frappe.get_doc("File", unlinked_files[file_url]["file"])
|
||||
file_data.attached_to_doctype = "Item"
|
||||
file_data.attached_to_name = item_code
|
||||
file_data.save()
|
||||
@@ -65,20 +65,20 @@ def rename_and_set_content_hash(files_path, unlinked_files, file_url):
|
||||
|
||||
# set content hash if missing
|
||||
file_data_name = unlinked_files[file_url]["file"]
|
||||
if not frappe.db.get_value("File Data", file_data_name, "content_hash"):
|
||||
if not frappe.db.get_value("File", file_data_name, "content_hash"):
|
||||
with open(new_filename, "r") as f:
|
||||
content_hash = get_content_hash(f.read())
|
||||
frappe.db.set_value("File Data", file_data_name, "content_hash", content_hash)
|
||||
frappe.db.set_value("File", file_data_name, "content_hash", content_hash)
|
||||
|
||||
def get_unlinked_files(files_path):
|
||||
# find files that have the same name as a File Data doc
|
||||
# and the file_name mentioned in that File Data doc doesn't exist
|
||||
# find files that have the same name as a File doc
|
||||
# and the file_name mentioned in that File doc doesn't exist
|
||||
# and it isn't already attached to a doc
|
||||
unlinked_files = {}
|
||||
files = os.listdir(files_path)
|
||||
for file in files:
|
||||
if not frappe.db.exists("File Data", {"file_name": file}):
|
||||
file_data = frappe.db.get_value("File Data", {"name": file},
|
||||
if not frappe.db.exists("File", {"file_name": file}):
|
||||
file_data = frappe.db.get_value("File", {"name": file},
|
||||
["file_name", "attached_to_doctype", "attached_to_name"], as_dict=True)
|
||||
|
||||
if (file_data
|
||||
|
||||
29
erpnext/patches/v5_8/tax_rule.py
Normal file
29
erpnext/patches/v5_8/tax_rule.py
Normal file
@@ -0,0 +1,29 @@
|
||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("accounts", "doctype", "tax_rule")
|
||||
|
||||
customers = frappe.db.sql("""select name, default_taxes_and_charges from tabCustomer where
|
||||
ifnull(default_taxes_and_charges, '') != '' """, as_dict=1)
|
||||
|
||||
for d in customers:
|
||||
tr = frappe.new_doc("Tax Rule")
|
||||
tr.tax_type = "Sales"
|
||||
tr.customer = d.name
|
||||
tr.sales_tax_template = d.default_taxes_and_charges
|
||||
tr.save()
|
||||
|
||||
|
||||
suppliers = frappe.db.sql("""select name, default_taxes_and_charges from tabSupplier where
|
||||
ifnull(default_taxes_and_charges, '') != '' """, as_dict=1)
|
||||
|
||||
for d in suppliers:
|
||||
tr = frappe.new_doc("Tax Rule")
|
||||
tr.tax_type = "Purchase"
|
||||
tr.supplier = d.name
|
||||
tr.purchase_tax_template = d.default_taxes_and_charges
|
||||
tr.save()
|
||||
@@ -63,56 +63,3 @@ def execute():
|
||||
where
|
||||
company=%s
|
||||
""", (company.default_currency, company.name))
|
||||
|
||||
# Set party account if default currency of party other than company's default currency
|
||||
for dt in ("Customer", "Supplier"):
|
||||
parties = frappe.get_all(dt, filters={"docstatus": 0})
|
||||
for p in parties:
|
||||
party = frappe.get_doc(dt, p.name)
|
||||
party_accounts = []
|
||||
|
||||
for company in company_list:
|
||||
# Get party GL Entries
|
||||
party_gle = frappe.db.get_value("GL Entry", {"party_type": dt, "party": p.name,
|
||||
"company": company.name}, ["account", "account_currency", "name"], as_dict=True)
|
||||
|
||||
# set party account currency
|
||||
if party_gle:
|
||||
party.party_account_currency = party_gle.account_currency
|
||||
elif not party.party_account_currency:
|
||||
party.party_account_currency = company.default_currency
|
||||
|
||||
# Add default receivable /payable account if not exists
|
||||
# and currency is other than company currency
|
||||
if party.party_account_currency and party.party_account_currency != company.default_currency:
|
||||
party_account_exists_for_company = False
|
||||
for d in party.get("accounts"):
|
||||
if d.company == company.name:
|
||||
account_currency = frappe.db.get_value("Account", d.account, "account_currency")
|
||||
if account_currency == party.party_account_currency:
|
||||
party_accounts.append({
|
||||
"company": d.company,
|
||||
"account": d.account
|
||||
})
|
||||
party_account_exists_for_company = True
|
||||
break
|
||||
|
||||
if not party_account_exists_for_company:
|
||||
party_account = None
|
||||
if party_gle:
|
||||
party_account = party_gle.account
|
||||
else:
|
||||
default_receivable_account_currency = frappe.db.get_value("Account",
|
||||
company.default_receivable_account, "account_currency")
|
||||
if default_receivable_account_currency != company.default_currency:
|
||||
party_account = company.default_receivable_account
|
||||
|
||||
if party_account:
|
||||
party_accounts.append({
|
||||
"company": company.name,
|
||||
"account": party_account
|
||||
})
|
||||
|
||||
party.set("accounts", party_accounts)
|
||||
party.flags.ignore_mandatory = True
|
||||
party.save()
|
||||
|
||||
@@ -2,6 +2,11 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
# remove missing lead
|
||||
for customer in frappe.db.sql_list("""select name from `tabCustomer`
|
||||
where ifnull(lead_name, '')!='' and not exists (select name from `tabLead` where name=`tabCustomer`.lead_name)"""):
|
||||
frappe.db.set_value("Customer", customer, "lead_name", None)
|
||||
|
||||
# remove missing default taxes
|
||||
for customer in frappe.db.sql_list("""select name from `tabCustomer`
|
||||
where ifnull(default_taxes_and_charges, '')!='' and not exists (
|
||||
@@ -18,8 +23,3 @@ def execute():
|
||||
c = frappe.get_doc("Supplier", supplier)
|
||||
c.default_taxes_and_charges = None
|
||||
c.save()
|
||||
|
||||
# remove missing lead
|
||||
for customer in frappe.db.sql_list("""select name from `tabCustomer`
|
||||
where ifnull(lead_name, '')!='' and not exists (select name from `tabLead` where name=`tabCustomer`.lead_name)"""):
|
||||
frappe.db.set_value("Customer", customer, "lead_name", None)
|
||||
|
||||
23
erpnext/patches/v6_3/convert_applicable_territory.py
Normal file
23
erpnext/patches/v6_3/convert_applicable_territory.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("stock", "doctype", "price_list_country")
|
||||
frappe.reload_doc("accounts", "doctype", "shipping_rule_country")
|
||||
frappe.reload_doctype("Price List")
|
||||
frappe.reload_doctype("Shipping Rule")
|
||||
frappe.reload_doctype("Shopping Cart Settings")
|
||||
|
||||
# for price list
|
||||
countries = frappe.db.sql_list("select name from tabCountry")
|
||||
|
||||
for doctype in ("Price List", "Shipping Rule"):
|
||||
for at in frappe.db.sql("""select name, parent, territory from `tabApplicable Territory` where
|
||||
parenttype = %s """, doctype, as_dict=True):
|
||||
if at.territory in countries:
|
||||
parent = frappe.get_doc(doctype, at.parent)
|
||||
if not parent.countries:
|
||||
parent.append("countries", {"country": at.territory})
|
||||
parent.save()
|
||||
|
||||
|
||||
frappe.delete_doc("DocType", "Applicable Territory")
|
||||
1
erpnext/patches/v6_4/__init__.py
Normal file
1
erpnext/patches/v6_4/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from __future__ import unicode_literals
|
||||
14
erpnext/patches/v6_4/round_status_updater_percentages.py
Normal file
14
erpnext/patches/v6_4/round_status_updater_percentages.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
for doctype, fieldname in (
|
||||
("Sales Order", "per_billed"),
|
||||
("Sales Order", "per_delivered"),
|
||||
("Delivery Note", "per_installed"),
|
||||
("Purchase Order", "per_billed"),
|
||||
("Purchase Order", "per_received"),
|
||||
("Material Request", "per_ordered"),
|
||||
):
|
||||
frappe.db.sql("""update `tab{doctype}` set `{fieldname}`=round(`{fieldname}`, 2)""".format(
|
||||
doctype=doctype, fieldname=fieldname))
|
||||
@@ -59,9 +59,15 @@ cur_frm.fields_dict.customer.get_query = function(doc,cdt,cdn) {
|
||||
}
|
||||
|
||||
cur_frm.fields_dict['sales_order'].get_query = function(doc) {
|
||||
var filters = {
|
||||
'project_name': ["in", doc.__islocal ? [""] : [doc.name, ""]]
|
||||
};
|
||||
|
||||
if (doc.customer) {
|
||||
filters["customer"] = doc.customer;
|
||||
}
|
||||
|
||||
return {
|
||||
filters:{
|
||||
'project_name': doc.name
|
||||
}
|
||||
filters: filters
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
frappe.listview_settings['Task'] = {
|
||||
add_fields: ["project", "status", "priority", "exp_end_date"],
|
||||
onload: function(listview) {
|
||||
frappe.route_options = {
|
||||
"status": "Open"
|
||||
};
|
||||
|
||||
var method = "erpnext.projects.doctype.task.task.set_multiple_status";
|
||||
|
||||
listview.page.add_menu_item(__("Set as Open"), function() {
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
background-repeat: no-repeat;
|
||||
background-position: center top;
|
||||
border-radius: 0.5em;
|
||||
border: 1px solid #ebeff2;
|
||||
}
|
||||
.product-image.missing-image {
|
||||
width: 100%;
|
||||
@@ -38,6 +39,7 @@
|
||||
background-repeat: no-repeat;
|
||||
background-position: center top;
|
||||
border-radius: 0.5em;
|
||||
border: 1px solid #ebeff2;
|
||||
border: 1px dashed #d1d8dd;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ $(function() {
|
||||
|
||||
$.extend(shopping_cart, {
|
||||
update_cart: function(opts) {
|
||||
if(!full_name) {
|
||||
if(!full_name || full_name==="Guest") {
|
||||
if(localStorage) {
|
||||
localStorage.setItem("last_visited", window.location.pathname);
|
||||
localStorage.setItem("pending_add_to_cart", opts.item_code);
|
||||
@@ -30,7 +30,7 @@ $.extend(shopping_cart, {
|
||||
args: {
|
||||
item_code: opts.item_code,
|
||||
qty: opts.qty,
|
||||
with_doc: opts.with_doc || 0
|
||||
with_items: opts.with_items || 0
|
||||
},
|
||||
btn: opts.btn,
|
||||
callback: function(r) {
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
// start
|
||||
$(document).on('startup', function() {
|
||||
console.log(__('Starting up...'));
|
||||
});
|
||||
@@ -49,18 +49,6 @@ $.extend(erpnext, {
|
||||
}
|
||||
},
|
||||
|
||||
add_applicable_territory: function() {
|
||||
if(cur_frm.doc.__islocal && (cur_frm.doc.territories || []).length===0) {
|
||||
var default_territory = frappe.defaults.get_user_default("territory");
|
||||
if(default_territory) {
|
||||
var territory = frappe.model.add_child(cur_frm.doc, "Applicable Territory",
|
||||
"territories");
|
||||
territory.territory = default_territory;
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
setup_serial_no: function() {
|
||||
var grid_row = cur_frm.open_grid_row();
|
||||
if(!grid_row.fields_dict.serial_no ||
|
||||
@@ -131,8 +119,8 @@ $.extend(erpnext.utils, {
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
copy_value_in_all_row: function(doc, dt, dn, table_fieldname, fieldname) {
|
||||
var d = locals[dt][dn];
|
||||
if(d[fieldname]){
|
||||
|
||||
@@ -20,6 +20,7 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) {
|
||||
price_list: frm.doc.buying_price_list
|
||||
};
|
||||
}
|
||||
args.posting_date = frm.doc.transaction_date;
|
||||
}
|
||||
if(!args) return;
|
||||
|
||||
@@ -42,6 +43,7 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) {
|
||||
|
||||
erpnext.utils.get_address_display = function(frm, address_field, display_field) {
|
||||
if(frm.updating_party_details) return;
|
||||
|
||||
if(!address_field) {
|
||||
if(frm.doc.customer) {
|
||||
address_field = "customer_address";
|
||||
@@ -49,14 +51,32 @@ erpnext.utils.get_address_display = function(frm, address_field, display_field)
|
||||
address_field = "supplier_address";
|
||||
} else return;
|
||||
}
|
||||
|
||||
if(!display_field) display_field = "address_display";
|
||||
if(frm.doc[address_field]) {
|
||||
frappe.call({
|
||||
method: "erpnext.utilities.doctype.address.address.get_address_display",
|
||||
args: {"address_dict": frm.doc[address_field] },
|
||||
callback: function(r) {
|
||||
if(r.message)
|
||||
if(r.message){
|
||||
frm.set_value(display_field, r.message)
|
||||
}
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.party.set_taxes",
|
||||
args: {
|
||||
"party": frm.doc.customer || frm.doc.supplier,
|
||||
"party_type": (frm.doc.customer ? "Customer" : "Supplier"),
|
||||
"posting_date": frm.doc.posting_date || frm.doc.transaction_date,
|
||||
"company": frm.doc.company,
|
||||
"billing_address": ((frm.doc.customer) ? (frm.doc.customer_address) : (frm.doc.supplier_address)),
|
||||
"shipping_address": frm.doc.shipping_address_name
|
||||
},
|
||||
callback: function(r) {
|
||||
if(r.message){
|
||||
frm.set_value("taxes_and_charges", r.message)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@border-color: #d1d8dd;
|
||||
@light-border-color: #EBEFF2;
|
||||
|
||||
.web-long-description {
|
||||
font-size: 18px;
|
||||
@@ -35,6 +36,7 @@
|
||||
background-repeat: no-repeat;
|
||||
background-position: center top;
|
||||
border-radius: 0.5em;
|
||||
border: 1px solid @light-border-color;
|
||||
}
|
||||
|
||||
.product-image.missing-image {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,11 +7,10 @@ from frappe.model.naming import make_autoname
|
||||
from frappe import _, msgprint, throw
|
||||
import frappe.defaults
|
||||
from frappe.utils import flt
|
||||
|
||||
from frappe.desk.reportview import build_match_conditions
|
||||
from erpnext.utilities.transaction_base import TransactionBase
|
||||
from erpnext.utilities.address_and_contact import load_address_and_contact
|
||||
from erpnext.accounts.party import validate_accounting_currency, validate_party_account
|
||||
from frappe.desk.reportview import build_match_conditions
|
||||
from erpnext.accounts.party import validate_party_accounts
|
||||
|
||||
class Customer(TransactionBase):
|
||||
def get_feed(self):
|
||||
@@ -33,8 +32,7 @@ class Customer(TransactionBase):
|
||||
|
||||
def validate(self):
|
||||
self.flags.is_new_doc = self.is_new()
|
||||
validate_accounting_currency(self)
|
||||
validate_party_account(self)
|
||||
validate_party_accounts(self)
|
||||
|
||||
def update_lead_status(self):
|
||||
if self.lead_name:
|
||||
@@ -197,27 +195,26 @@ def get_customer_outstanding(customer, company):
|
||||
outstanding_based_on_so = flt(outstanding_based_on_so[0][0]) if outstanding_based_on_so else 0.0
|
||||
|
||||
# Outstanding based on Delivery Note
|
||||
outstanding_based_on_dn = frappe.db.sql("""
|
||||
select
|
||||
sum(
|
||||
(
|
||||
(ifnull(dn_item.amount, 0) - ifnull((select sum(ifnull(amount, 0))
|
||||
from `tabSales Invoice Item`
|
||||
where ifnull(dn_detail, '') = dn_item.name and docstatus = 1), 0)
|
||||
)/dn.base_net_total
|
||||
)*dn.base_grand_total
|
||||
)
|
||||
unmarked_delivery_note_items = frappe.db.sql("""select
|
||||
dn_item.name, dn_item.amount, dn.base_net_total, dn.base_grand_total
|
||||
from `tabDelivery Note` dn, `tabDelivery Note Item` dn_item
|
||||
where
|
||||
dn.name = dn_item.parent and dn.customer=%s and dn.company=%s
|
||||
dn.name = dn_item.parent
|
||||
and dn.customer=%s and dn.company=%s
|
||||
and dn.docstatus = 1 and dn.status != 'Stopped'
|
||||
and ifnull(dn_item.against_sales_order, '') = ''
|
||||
and ifnull(dn_item.against_sales_invoice, '') = ''
|
||||
and ifnull(dn_item.amount, 0) > ifnull((select sum(ifnull(amount, 0))
|
||||
from `tabSales Invoice Item`
|
||||
where ifnull(dn_detail, '') = dn_item.name and docstatus = 1), 0)""", (customer, company))
|
||||
and ifnull(dn_item.against_sales_invoice, '') = ''""", (customer, company), as_dict=True)
|
||||
|
||||
outstanding_based_on_dn = flt(outstanding_based_on_dn[0][0]) if outstanding_based_on_dn else 0.0
|
||||
outstanding_based_on_dn = 0.0
|
||||
|
||||
for dn_item in unmarked_delivery_note_items:
|
||||
si_amount = frappe.db.sql("""select sum(ifnull(amount, 0))
|
||||
from `tabSales Invoice Item`
|
||||
where dn_detail = %s and docstatus = 1""", dn_item.name)[0][0]
|
||||
|
||||
if flt(dn_item.amount) > flt(si_amount):
|
||||
outstanding_based_on_dn += ((flt(dn_item.amount) - flt(si_amount)) \
|
||||
/ dn_item.base_net_total) * dn_item.base_grand_total
|
||||
|
||||
return outstanding_based_on_gle + outstanding_based_on_so + outstanding_based_on_dn
|
||||
|
||||
|
||||
@@ -7,9 +7,7 @@ import frappe
|
||||
import unittest
|
||||
|
||||
from frappe.test_runner import make_test_records
|
||||
from erpnext.controllers.accounts_controller import CustomerFrozen
|
||||
from erpnext.accounts.party import InvalidCurrency
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
from erpnext.exceptions import CustomerFrozen
|
||||
|
||||
test_ignore = ["Price List"]
|
||||
|
||||
@@ -37,9 +35,9 @@ class TestCustomer(unittest.TestCase):
|
||||
|
||||
make_test_records("Address")
|
||||
make_test_records("Contact")
|
||||
frappe.db.set_value("Contact", "_Test Contact For _Test Customer-_Test Customer",
|
||||
frappe.db.set_value("Contact", "_Test Contact For _Test Customer-_Test Customer",
|
||||
"is_primary_contact", 1)
|
||||
|
||||
|
||||
details = get_party_details("_Test Customer")
|
||||
|
||||
for key, value in to_check.iteritems():
|
||||
@@ -68,25 +66,15 @@ class TestCustomer(unittest.TestCase):
|
||||
{"comment_doctype": "Customer", "comment_docname": "_Test Customer 1 Renamed"}), comment.name)
|
||||
|
||||
frappe.rename_doc("Customer", "_Test Customer 1 Renamed", "_Test Customer 1")
|
||||
|
||||
|
||||
def test_freezed_customer(self):
|
||||
frappe.db.set_value("Customer", "_Test Customer", "is_frozen", 1)
|
||||
|
||||
|
||||
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
|
||||
|
||||
|
||||
so = make_sales_order(do_not_save= True)
|
||||
self.assertRaises(CustomerFrozen, so.save)
|
||||
|
||||
|
||||
frappe.db.set_value("Customer", "_Test Customer", "is_frozen", 0)
|
||||
|
||||
|
||||
so.save()
|
||||
|
||||
def test_multi_currency(self):
|
||||
customer = frappe.get_doc("Customer", "_Test Customer USD")
|
||||
|
||||
create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
|
||||
currency="USD", conversion_rate=50)
|
||||
|
||||
customer.party_account_currency = "EUR"
|
||||
self.assertRaises(InvalidCurrency, customer.save)
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
"customer_type": "Individual",
|
||||
"doctype": "Customer",
|
||||
"territory": "_Test Territory",
|
||||
"party_account_currency": "USD",
|
||||
"accounts": [{
|
||||
"company": "_Test Company",
|
||||
"account": "_Test Receivable USD - _TC"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,17 +1,20 @@
|
||||
frappe.listview_settings['Sales Order'] = {
|
||||
add_fields: ["base_grand_total", "customer_name", "currency", "delivery_date", "per_delivered", "per_billed",
|
||||
"status"],
|
||||
"status", "order_type"],
|
||||
get_indicator: function(doc) {
|
||||
if(doc.status==="Stopped") {
|
||||
return [__("Stopped"), "darkgrey", "status,=,Stopped"];
|
||||
} else if(flt(doc.per_delivered) < 100 && frappe.datetime.get_diff(doc.delivery_date) < 0) {
|
||||
|
||||
} else if (doc.order_type !== "Maintenance"
|
||||
&& flt(doc.per_delivered, 2) < 100 && frappe.datetime.get_diff(doc.delivery_date) < 0) {
|
||||
// to bill & overdue
|
||||
return [__("Overdue"), "red", "per_delivered,<,100|delivery_date,<,Today|status,!=,Stopped"];
|
||||
|
||||
} else if(flt(doc.per_delivered) < 100 && doc.status!=="Stopped") {
|
||||
} else if (doc.order_type !== "Maintenance"
|
||||
&& flt(doc.per_delivered, 2) < 100 && doc.status!=="Stopped") {
|
||||
// not delivered
|
||||
|
||||
if(flt(doc.per_billed) < 100) {
|
||||
if(flt(doc.per_billed, 2) < 100) {
|
||||
// not delivered & not billed
|
||||
|
||||
return [__("To Deliver and Bill"), "orange",
|
||||
@@ -23,11 +26,14 @@ frappe.listview_settings['Sales Order'] = {
|
||||
"per_delivered,<,100|per_billed,=,100|status,!=,Stopped"];
|
||||
}
|
||||
|
||||
} else if(flt(doc.per_delivered) == 100 && flt(doc.per_billed) < 100 && doc.status!=="Stopped") {
|
||||
} else if ((doc.order_type === "Maintenance" || flt(doc.per_delivered, 2) == 100)
|
||||
&& flt(doc.per_billed, 2) < 100 && doc.status!=="Stopped") {
|
||||
|
||||
// to bill
|
||||
return [__("To Bill"), "orange", "per_delivered,=,100|per_billed,<,100|status,!=,Stopped"];
|
||||
|
||||
} else if(flt(doc.per_delivered) == 100 && flt(doc.per_billed) == 100 && doc.status!=="Stopped") {
|
||||
} else if((doc.order_type === "Maintenance" || flt(doc.per_delivered, 2) == 100)
|
||||
&& flt(doc.per_billed, 2) == 100 && doc.status!=="Stopped") {
|
||||
|
||||
return [__("Completed"), "green", "per_delivered,=,100|per_billed,=,100|status,!=,Stopped"];
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import frappe.permissions
|
||||
import unittest
|
||||
from erpnext.selling.doctype.sales_order.sales_order \
|
||||
import make_material_request, make_delivery_note, make_sales_invoice, WarehouseRequired
|
||||
from erpnext.accounts.doctype.journal_entry.test_journal_entry \
|
||||
import make_journal_entry
|
||||
|
||||
from frappe.tests.test_permissions import set_user_permission_doctypes
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"doctype": "DocType",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "sales_person",
|
||||
@@ -34,7 +34,7 @@
|
||||
"width": "200px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "contact_no",
|
||||
@@ -79,7 +79,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "allocated_percentage",
|
||||
@@ -104,7 +104,7 @@
|
||||
"width": "100px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "allocated_amount",
|
||||
@@ -130,7 +130,7 @@
|
||||
"width": "120px"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "incentives",
|
||||
@@ -185,7 +185,7 @@
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"modified": "2015-08-13 16:30:24.146848",
|
||||
"modified": "2015-09-23 09:50:26.923187",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Sales Team",
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Settings to manage automated backups to third party tools like Dropbox and Google Drive.
|
||||
@@ -1,155 +0,0 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
# SETUP:
|
||||
# install pip install --upgrade dropbox
|
||||
#
|
||||
# Create new Dropbox App
|
||||
#
|
||||
# in conf.py, set oauth2 settings
|
||||
# dropbox_access_key
|
||||
# dropbox_access_secret
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import os
|
||||
import frappe
|
||||
from frappe.utils import get_request_site_address, cstr
|
||||
from frappe import _
|
||||
|
||||
ignore_list = [".DS_Store"]
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_dropbox_authorize_url():
|
||||
sess = get_dropbox_session()
|
||||
request_token = sess.obtain_request_token()
|
||||
return_address = get_request_site_address(True) \
|
||||
+ "?cmd=erpnext.setup.doctype.backup_manager.backup_dropbox.dropbox_callback"
|
||||
|
||||
url = sess.build_authorize_url(request_token, return_address)
|
||||
|
||||
return {
|
||||
"url": url,
|
||||
"key": request_token.key,
|
||||
"secret": request_token.secret,
|
||||
}
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def dropbox_callback(oauth_token=None, not_approved=False):
|
||||
from dropbox import client
|
||||
if not not_approved:
|
||||
if frappe.db.get_value("Backup Manager", None, "dropbox_access_key")==oauth_token:
|
||||
allowed = 1
|
||||
message = "Dropbox access allowed."
|
||||
|
||||
sess = get_dropbox_session()
|
||||
sess.set_request_token(frappe.db.get_value("Backup Manager", None, "dropbox_access_key"),
|
||||
frappe.db.get_value("Backup Manager", None, "dropbox_access_secret"))
|
||||
access_token = sess.obtain_access_token()
|
||||
frappe.db.set_value("Backup Manager", "Backup Manager", "dropbox_access_key", access_token.key)
|
||||
frappe.db.set_value("Backup Manager", "Backup Manager", "dropbox_access_secret", access_token.secret)
|
||||
frappe.db.set_value("Backup Manager", "Backup Manager", "dropbox_access_allowed", allowed)
|
||||
frappe.db.set_value("Backup Manager", "Backup Manager", "send_backups_to_dropbox", 1)
|
||||
dropbox_client = client.DropboxClient(sess)
|
||||
try:
|
||||
dropbox_client.file_create_folder("files")
|
||||
except:
|
||||
pass
|
||||
|
||||
else:
|
||||
allowed = 0
|
||||
message = "Illegal Access Token Please try again."
|
||||
else:
|
||||
allowed = 0
|
||||
message = "Dropbox Access not approved."
|
||||
|
||||
frappe.local.message_title = "Dropbox Approval"
|
||||
frappe.local.message = "<h3>%s</h3><p>Please close this window.</p>" % message
|
||||
|
||||
if allowed:
|
||||
frappe.local.message_success = True
|
||||
|
||||
frappe.db.commit()
|
||||
frappe.response['type'] = 'page'
|
||||
frappe.response['page_name'] = 'message.html'
|
||||
|
||||
def backup_to_dropbox():
|
||||
from dropbox import client, session
|
||||
from frappe.utils.backups import new_backup
|
||||
from frappe.utils import get_files_path, get_backups_path
|
||||
if not frappe.db:
|
||||
frappe.connect()
|
||||
|
||||
sess = session.DropboxSession(frappe.conf.dropbox_access_key, frappe.conf.dropbox_secret_key, "app_folder")
|
||||
|
||||
sess.set_token(frappe.db.get_value("Backup Manager", None, "dropbox_access_key"),
|
||||
frappe.db.get_value("Backup Manager", None, "dropbox_access_secret"))
|
||||
|
||||
dropbox_client = client.DropboxClient(sess)
|
||||
|
||||
# upload database
|
||||
backup = new_backup()
|
||||
filename = os.path.join(get_backups_path(), os.path.basename(backup.backup_path_db))
|
||||
upload_file_to_dropbox(filename, "/database", dropbox_client)
|
||||
|
||||
frappe.db.close()
|
||||
response = dropbox_client.metadata("/files")
|
||||
|
||||
# upload files to files folder
|
||||
did_not_upload = []
|
||||
error_log = []
|
||||
path = get_files_path()
|
||||
for filename in os.listdir(path):
|
||||
filename = cstr(filename)
|
||||
|
||||
if filename in ignore_list:
|
||||
continue
|
||||
|
||||
found = False
|
||||
filepath = os.path.join(path, filename)
|
||||
for file_metadata in response["contents"]:
|
||||
if os.path.basename(filepath) == os.path.basename(file_metadata["path"]) and os.stat(filepath).st_size == int(file_metadata["bytes"]):
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
try:
|
||||
upload_file_to_dropbox(filepath, "/files", dropbox_client)
|
||||
except Exception:
|
||||
did_not_upload.append(filename)
|
||||
error_log.append(frappe.get_traceback())
|
||||
|
||||
frappe.connect()
|
||||
return did_not_upload, list(set(error_log))
|
||||
|
||||
def get_dropbox_session():
|
||||
try:
|
||||
from dropbox import session
|
||||
except:
|
||||
frappe.msgprint(_("Please install dropbox python module"), raise_exception=1)
|
||||
|
||||
if not (frappe.conf.dropbox_access_key or frappe.conf.dropbox_secret_key):
|
||||
frappe.throw(_("Please set Dropbox access keys in your site config"))
|
||||
|
||||
sess = session.DropboxSession(frappe.conf.dropbox_access_key, frappe.conf.dropbox_secret_key, "app_folder")
|
||||
return sess
|
||||
|
||||
def upload_file_to_dropbox(filename, folder, dropbox_client):
|
||||
from dropbox import rest
|
||||
size = os.stat(filename).st_size
|
||||
|
||||
with open(filename, 'r') as f:
|
||||
# if max packet size reached, use chunked uploader
|
||||
max_packet_size = 4194304
|
||||
|
||||
if size > max_packet_size:
|
||||
uploader = dropbox_client.get_chunked_uploader(f, size)
|
||||
while uploader.offset < size:
|
||||
try:
|
||||
uploader.upload_chunked()
|
||||
uploader.finish(folder + "/" + os.path.basename(filename), overwrite=True)
|
||||
except rest.ErrorResponse:
|
||||
pass
|
||||
else:
|
||||
dropbox_client.put_file(folder + "/" + os.path.basename(filename), f, overwrite=True)
|
||||
|
||||
if __name__=="__main__":
|
||||
backup_to_dropbox()
|
||||
@@ -1,30 +0,0 @@
|
||||
<table class="table table-striped" style="max-width: 600px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 30%;">
|
||||
{{ __("Date") }}
|
||||
</th>
|
||||
<th style="width: 50%;">
|
||||
{{ __("File") }}
|
||||
</th>
|
||||
<th>
|
||||
{{ __("Size") }}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for (var i=0; i < files.length; i++) { %}
|
||||
<tr>
|
||||
<td>
|
||||
{{ files[i][1] }}
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ files[i][0] }}" target="_blank">{{ files[i][0] }}</a>
|
||||
</td>
|
||||
<td>
|
||||
{{ files[i][2] }}
|
||||
</td>
|
||||
</tr>
|
||||
{% } %}
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -1,172 +0,0 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
# SETUP:
|
||||
# install pip install --upgrade google-api-python-client
|
||||
#
|
||||
# In Google API
|
||||
# - create new API project
|
||||
# - create new oauth2 client (create installed app type as google \
|
||||
# does not support subdomains)
|
||||
#
|
||||
# in conf.py, set oauth2 settings
|
||||
# gdrive_client_id
|
||||
# gdrive_client_secret
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import httplib2
|
||||
import os
|
||||
import mimetypes
|
||||
import frappe
|
||||
import oauth2client.client
|
||||
from frappe.utils import cstr
|
||||
from frappe import _
|
||||
from apiclient.discovery import build
|
||||
from apiclient.http import MediaFileUpload
|
||||
|
||||
# define log config for google drive api's log messages
|
||||
# basicConfig redirects log to stderr
|
||||
import logging
|
||||
logging.basicConfig()
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_gdrive_authorize_url():
|
||||
flow = get_gdrive_flow()
|
||||
authorize_url = flow.step1_get_authorize_url()
|
||||
return {
|
||||
"authorize_url": authorize_url,
|
||||
}
|
||||
|
||||
def upload_files(name, mimetype, service, folder_id):
|
||||
if not frappe.db:
|
||||
frappe.connect()
|
||||
file_name = os.path.basename(name)
|
||||
media_body = MediaFileUpload(name, mimetype=mimetype, resumable=True)
|
||||
body = {
|
||||
'title': file_name,
|
||||
'description': 'Backup File',
|
||||
'mimetype': mimetype,
|
||||
'parents': [{
|
||||
'kind': 'drive#filelink',
|
||||
'id': folder_id
|
||||
}]
|
||||
}
|
||||
request = service.files().insert(body=body, media_body=media_body)
|
||||
response = None
|
||||
while response is None:
|
||||
status, response = request.next_chunk()
|
||||
|
||||
def backup_to_gdrive():
|
||||
from frappe.utils.backups import new_backup
|
||||
if not frappe.db:
|
||||
frappe.connect()
|
||||
get_gdrive_flow()
|
||||
credentials_json = frappe.db.get_value("Backup Manager", None, "gdrive_credentials")
|
||||
credentials = oauth2client.client.Credentials.new_from_json(credentials_json)
|
||||
http = httplib2.Http()
|
||||
http = credentials.authorize(http)
|
||||
drive_service = build('drive', 'v2', http=http)
|
||||
|
||||
# upload database
|
||||
backup = new_backup()
|
||||
path = os.path.join(frappe.local.site_path, "public", "backups")
|
||||
filename = os.path.join(path, os.path.basename(backup.backup_path_db))
|
||||
|
||||
# upload files to database folder
|
||||
upload_files(filename, 'application/x-gzip', drive_service,
|
||||
frappe.db.get_value("Backup Manager", None, "database_folder_id"))
|
||||
|
||||
# upload files to files folder
|
||||
did_not_upload = []
|
||||
error_log = []
|
||||
|
||||
files_folder_id = frappe.db.get_value("Backup Manager", None, "files_folder_id")
|
||||
|
||||
frappe.db.close()
|
||||
path = os.path.join(frappe.local.site_path, "public", "files")
|
||||
for filename in os.listdir(path):
|
||||
filename = cstr(filename)
|
||||
found = False
|
||||
filepath = os.path.join(path, filename)
|
||||
ext = filename.split('.')[-1]
|
||||
size = os.path.getsize(filepath)
|
||||
if ext == 'gz' or ext == 'gzip':
|
||||
mimetype = 'application/x-gzip'
|
||||
else:
|
||||
mimetype = mimetypes.types_map.get("." + ext) or "application/octet-stream"
|
||||
|
||||
#Compare Local File with Server File
|
||||
children = drive_service.children().list(folderId=files_folder_id).execute()
|
||||
for child in children.get('items', []):
|
||||
file = drive_service.files().get(fileId=child['id']).execute()
|
||||
if filename == file['title'] and size == int(file['fileSize']):
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
try:
|
||||
upload_files(filepath, mimetype, drive_service, files_folder_id)
|
||||
except Exception, e:
|
||||
did_not_upload.append(filename)
|
||||
error_log.append(cstr(e))
|
||||
|
||||
frappe.connect()
|
||||
return did_not_upload, list(set(error_log))
|
||||
|
||||
def get_gdrive_flow():
|
||||
from oauth2client.client import OAuth2WebServerFlow
|
||||
from frappe import conf
|
||||
|
||||
if not "gdrive_client_id" in conf:
|
||||
frappe.throw(_("Please set Google Drive access keys in {0}"),format("site_config.json"))
|
||||
|
||||
flow = OAuth2WebServerFlow(conf.gdrive_client_id, conf.gdrive_client_secret,
|
||||
"https://www.googleapis.com/auth/drive", 'urn:ietf:wg:oauth:2.0:oob')
|
||||
return flow
|
||||
|
||||
@frappe.whitelist()
|
||||
def gdrive_callback(verification_code = None):
|
||||
flow = get_gdrive_flow()
|
||||
if verification_code:
|
||||
credentials = flow.step2_exchange(verification_code)
|
||||
allowed = 1
|
||||
|
||||
# make folders to save id
|
||||
http = httplib2.Http()
|
||||
http = credentials.authorize(http)
|
||||
drive_service = build('drive', 'v2', http=http)
|
||||
erpnext_folder_id = create_erpnext_folder(drive_service)
|
||||
database_folder_id = create_folder('database', drive_service, erpnext_folder_id)
|
||||
files_folder_id = create_folder('files', drive_service, erpnext_folder_id)
|
||||
|
||||
frappe.db.set_value("Backup Manager", "Backup Manager", "gdrive_access_allowed", allowed)
|
||||
frappe.db.set_value("Backup Manager", "Backup Manager", "database_folder_id", database_folder_id)
|
||||
frappe.db.set_value("Backup Manager", "Backup Manager", "files_folder_id", files_folder_id)
|
||||
final_credentials = credentials.to_json()
|
||||
frappe.db.set_value("Backup Manager", "Backup Manager", "gdrive_credentials", final_credentials)
|
||||
|
||||
frappe.msgprint(_("Updated"))
|
||||
|
||||
def create_erpnext_folder(service):
|
||||
if not frappe.db:
|
||||
frappe.connect()
|
||||
erpnext = {
|
||||
'title': 'erpnext',
|
||||
'mimeType': 'application/vnd.google-apps.folder'
|
||||
}
|
||||
erpnext = service.files().insert(body=erpnext).execute()
|
||||
return erpnext['id']
|
||||
|
||||
def create_folder(name, service, folder_id):
|
||||
database = {
|
||||
'title': name,
|
||||
'mimeType': 'application/vnd.google-apps.folder',
|
||||
'parents': [{
|
||||
'kind': 'drive#fileLink',
|
||||
'id': folder_id
|
||||
}]
|
||||
}
|
||||
database = service.files().insert(body=database).execute()
|
||||
return database['id']
|
||||
|
||||
if __name__=="__main__":
|
||||
backup_to_gdrive()
|
||||
@@ -1,73 +0,0 @@
|
||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
$.extend(cur_frm.cscript, {
|
||||
onload_post_render: function() {
|
||||
cur_frm.fields_dict.allow_dropbox_access.$input.addClass("btn-primary");
|
||||
|
||||
if(cur_frm.doc.__onload && cur_frm.doc.__onload.files) {
|
||||
$(frappe.render_template("backup_files_list", {files:cur_frm.doc.__onload.files}))
|
||||
.appendTo(cur_frm.fields_dict.current_backups.$wrapper.empty());
|
||||
}
|
||||
},
|
||||
refresh: function() {
|
||||
cur_frm.disable_save();
|
||||
},
|
||||
|
||||
validate_send_notifications_to: function() {
|
||||
if(!cur_frm.doc.send_notifications_to) {
|
||||
msgprint(__("Please specify") + ": " +
|
||||
__(frappe.meta.get_label(cur_frm.doctype, "send_notifications_to")));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
allow_dropbox_access: function() {
|
||||
if(cur_frm.cscript.validate_send_notifications_to()) {
|
||||
return frappe.call({
|
||||
method: "erpnext.setup.doctype.backup_manager.backup_dropbox.get_dropbox_authorize_url",
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
cur_frm.set_value("dropbox_access_secret", r.message.secret);
|
||||
cur_frm.set_value("dropbox_access_key", r.message.key);
|
||||
cur_frm.save(null, function() {
|
||||
window.open(r.message.url);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
allow_gdrive_access: function() {
|
||||
if(cur_frm.cscript.validate_send_notifications_to()) {
|
||||
return frappe.call({
|
||||
method: "erpnext.setup.doctype.backup_manager.backup_googledrive.get_gdrive_authorize_url",
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
window.open(r.message.authorize_url);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
validate_gdrive: function() {
|
||||
return frappe.call({
|
||||
method: "erpnext.setup.doctype.backup_manager.backup_googledrive.gdrive_callback",
|
||||
args: {
|
||||
verification_code: cur_frm.doc.verification_code
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
upload_backups_to_dropbox: function() {
|
||||
cur_frm.save();
|
||||
},
|
||||
|
||||
// upload_backups_to_gdrive: function() {
|
||||
// cur_frm.save();
|
||||
// },
|
||||
});
|
||||
@@ -1,102 +0,0 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from frappe.utils import get_site_path, cint, split_emails
|
||||
from frappe.utils.data import convert_utc_to_user_timezone
|
||||
import os
|
||||
import datetime
|
||||
import frappe
|
||||
|
||||
from frappe.model.document import Document
|
||||
|
||||
class BackupManager(Document):
|
||||
def onload(self):
|
||||
self.set_onload("files", get_files())
|
||||
|
||||
def get_files():
|
||||
def get_time(path):
|
||||
dt = os.path.getmtime(path)
|
||||
return convert_utc_to_user_timezone(datetime.datetime.utcfromtimestamp(dt)).strftime('%Y-%m-%d %H:%M')
|
||||
|
||||
def get_size(path):
|
||||
size = os.path.getsize(path)
|
||||
if size > 1048576:
|
||||
return "{0:.1f}M".format(float(size) / 1048576)
|
||||
else:
|
||||
return "{0:.1f}K".format(float(size) / 1024)
|
||||
|
||||
path = get_site_path('private', 'backups')
|
||||
files = [x for x in os.listdir(path) if os.path.isfile(os.path.join(path, x))]
|
||||
files = [('/backups/' + _file,
|
||||
get_time(os.path.join(path, _file)),
|
||||
get_size(os.path.join(path, _file))) for _file in files]
|
||||
return files
|
||||
|
||||
def take_backups_daily():
|
||||
take_backups_if("Daily")
|
||||
|
||||
def take_backups_weekly():
|
||||
take_backups_if("Weekly")
|
||||
|
||||
def take_backups_if(freq):
|
||||
if cint(frappe.db.get_value("Backup Manager", None, "send_backups_to_dropbox")):
|
||||
if frappe.db.get_value("Backup Manager", None, "upload_backups_to_dropbox")==freq:
|
||||
take_backups_dropbox()
|
||||
|
||||
# if frappe.db.get_value("Backup Manager", None, "upload_backups_to_gdrive")==freq:
|
||||
# take_backups_gdrive()
|
||||
|
||||
@frappe.whitelist()
|
||||
def take_backups_dropbox():
|
||||
did_not_upload, error_log = [], []
|
||||
try:
|
||||
from erpnext.setup.doctype.backup_manager.backup_dropbox import backup_to_dropbox
|
||||
did_not_upload, error_log = backup_to_dropbox()
|
||||
if did_not_upload: raise Exception
|
||||
|
||||
send_email(True, "Dropbox")
|
||||
except Exception:
|
||||
file_and_error = [" - ".join(f) for f in zip(did_not_upload, error_log)]
|
||||
error_message = ("\n".join(file_and_error) + "\n" + frappe.get_traceback())
|
||||
frappe.errprint(error_message)
|
||||
send_email(False, "Dropbox", error_message)
|
||||
|
||||
#backup to gdrive
|
||||
@frappe.whitelist()
|
||||
def take_backups_gdrive():
|
||||
did_not_upload, error_log = [], []
|
||||
try:
|
||||
from erpnext.setup.doctype.backup_manager.backup_googledrive import backup_to_gdrive
|
||||
did_not_upload, error_log = backup_to_gdrive()
|
||||
if did_not_upload: raise Exception
|
||||
|
||||
send_email(True, "Google Drive")
|
||||
except Exception:
|
||||
file_and_error = [" - ".join(f) for f in zip(did_not_upload, error_log)]
|
||||
error_message = ("\n".join(file_and_error) + "\n" + frappe.get_traceback())
|
||||
frappe.errprint(error_message)
|
||||
send_email(False, "Google Drive", error_message)
|
||||
|
||||
def send_email(success, service_name, error_status=None):
|
||||
if success:
|
||||
subject = "Backup Upload Successful"
|
||||
message ="""<h3>Backup Uploaded Successfully</h3><p>Hi there, this is just to inform you
|
||||
that your backup was successfully uploaded to your %s account. So relax!</p>
|
||||
""" % service_name
|
||||
|
||||
else:
|
||||
subject = "[Warning] Backup Upload Failed"
|
||||
message ="""<h3>Backup Upload Failed</h3><p>Oops, your automated backup to %s
|
||||
failed.</p>
|
||||
<p>Error message: %s</p>
|
||||
<p>Please contact your system manager for more information.</p>
|
||||
""" % (service_name, error_status)
|
||||
|
||||
if not frappe.db:
|
||||
frappe.connect()
|
||||
|
||||
recipients = split_emails(frappe.db.get_value("Backup Manager", None, "send_notifications_to"))
|
||||
frappe.sendmail(recipients=recipients, subject=subject, message=message)
|
||||
@@ -46,13 +46,13 @@ class Company(Document):
|
||||
if for_company != self.name:
|
||||
frappe.throw(_("Account {0} does not belong to company: {1}")
|
||||
.format(self.get(field), self.name))
|
||||
|
||||
|
||||
def validate_currency(self):
|
||||
self.previous_default_currency = frappe.db.get_value("Company", self.name, "default_currency")
|
||||
if self.default_currency and self.previous_default_currency and \
|
||||
self.default_currency != self.previous_default_currency and \
|
||||
self.check_if_transactions_exist():
|
||||
frappe.throw(_("Cannot change company's default currency, because there are existing transactions. Transactions must be cancelled to change the default currency."))
|
||||
frappe.throw(_("Cannot change company's default currency, because there are existing transactions. Transactions must be cancelled to change the default currency."))
|
||||
|
||||
def on_update(self):
|
||||
if not frappe.db.sql("""select name from tabAccount
|
||||
@@ -208,7 +208,7 @@ class Company(Document):
|
||||
|
||||
# clear default accounts, warehouses from item
|
||||
if warehouses:
|
||||
|
||||
|
||||
for f in ["default_warehouse", "website_warehouse"]:
|
||||
frappe.db.sql("""update tabItem set %s=NULL where %s in (%s)"""
|
||||
% (f, f, ', '.join(['%s']*len(warehouses))), tuple(warehouses))
|
||||
@@ -257,3 +257,7 @@ def get_name_with_abbr(name, company):
|
||||
parts.append(company_abbr)
|
||||
|
||||
return " - ".join(parts)
|
||||
|
||||
def get_company_currency(company):
|
||||
return frappe.local_cache("company_currency", company,
|
||||
lambda: frappe.db.get_value("Company", company, "default_currency"))
|
||||
|
||||
@@ -4,5 +4,11 @@
|
||||
"doctype": "Customer Group",
|
||||
"is_group": "No",
|
||||
"parent_customer_group": "All Customer Groups"
|
||||
},
|
||||
{
|
||||
"customer_group_name": "_Test Customer Group 1",
|
||||
"doctype": "Customer Group",
|
||||
"is_group": "No",
|
||||
"parent_customer_group": "All Customer Groups"
|
||||
}
|
||||
]
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user