Merge branch 'develop' into scheduling-ui-rewrite

This commit is contained in:
Pranav Nachnekar 2019-11-04 04:38:59 +00:00 committed by GitHub
commit af18b2cdc5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
148 changed files with 1130 additions and 777 deletions

47
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,47 @@
---
name: Bug report
about: Report a bug encountered while using ERPNext
labels: bug
---
<!--
Welcome to ERPNext issue tracker! Before creating an issue, please heed the following:
1. This tracker should only be used to report bugs and request features / enhancements to ERPNext
- For questions and general support, checkout the manual https://erpnext.com/docs/user/manual/en or use https://discuss.erpnext.com
- For documentation issues, refer to https://github.com/frappe/erpnext_com
2. Use the search function before creating a new issue. Duplicates will be closed and directed to
the original discussion.
3. When making a bug report, make sure you provide all required information. The easier it is for
maintainers to reproduce, the faster it'll be fixed.
4. If you think you know what the reason for the bug is, share it with us. Maybe put in a PR 😉
-->
## Description of the issue
## Context information (for bug reports)
**Output of `bench version`**
```
(paste here)
```
## Steps to reproduce the issue
1.
2.
3.
### Observed result
### Expected result
### Stacktrace / full error message
```
(paste here)
```
## Additional information
OS version / distribution, `ERPNext` install method, etc.

View File

@ -0,0 +1,28 @@
---
name: Feature request
about: Suggest an idea to improve ERPNext
labels: feature-request
---
<!--
Welcome to ERPNext issue tracker! Before creating an issue, please heed the following:
1. This tracker should only be used to report bugs and request features / enhancements to ERPNext
- For questions and general support, checkout the manual https://erpnext.com/docs/user/manual/en or use https://discuss.erpnext.com
- For documentation issues, refer to https://github.com/frappe/erpnext_com
2. Use the search function before creating a new issue. Duplicates will be closed and directed to
the original discussion.
3. When making a feature request, make sure to be as verbose as possible. The better you convey your message, the greater the drive to make it happen.
-->
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -0,0 +1,17 @@
---
name: Question about using ERPNext
about: This is not the appropriate channel
labels: invalid
---
Please post on our forums:
for questions about using `ERPNext`: https://discuss.erpnext.com
for questions about using the `Frappe Framework`: https://discuss.frappe.io
for questions about using `bench`, probably the best place to start is the [bench repo](https://github.com/frappe/bench)
For documentation issues, use the [ERPNext Documentation](https://erpnext.com/docs/) or [Frappe Framework Documentation](https://frappe.io/docs/user/en) or the [developer cheetsheet](https://github.com/frappe/frappe/wiki/Developer-Cheatsheet)
> **Posts that are not bug reports or feature requests will not be addressed on this issue tracker.**

View File

@ -1,2 +1,33 @@
Please read the pull request checklist to make sure your changes are merged: https://github.com/frappe/erpnext/wiki/Pull-Request-Checklist <!--
Some key notes before you open a PR:
1. Select which branch should this PR be merged in?
2. PR name follows [convention](http://karma-runner.github.io/4.0/dev/git-commit-msg.html)
3. All tests pass locally, UI and Unit tests
4. All business logic and validations must be on the server-side
5. Update necessary Documentation
6. Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes
Also, if you're new here
- Documentation Guidelines => https://github.com/frappe/erpnext/wiki/Updating-Documentation
- Contribution Guide => https://github.com/frappe/erpnext/blob/develop/.github/CONTRIBUTING.md
- Pull Request Checklist => https://github.com/frappe/erpnext/wiki/Pull-Request-Checklist
-->
> Please provide enough information so that others can review your pull request:
<!-- You can skip this if you're fixing a typo or updating existing documentation -->
> Explain the **details** for making this change. What existing problem does the pull request solve?
<!-- Example: When "Adding a function to do X", explain why it is necessary to have a way to do X. -->
> Screenshots/GIFs
<!-- Add images/recordings to better visualize the change: expected/current behviour -->

7
SECURITY.md Normal file
View File

@ -0,0 +1,7 @@
# Security Policy
The ERPNext team and community take security issues seriously. To report a security issue, fill out the form at [https://erpnext.com/security/report](https://erpnext.com/security/report).
You can help us make ERPNext and all it's users more secure by following the [Reporting guidelines](https://erpnext.com/security).
We appreciate your efforts to responsibly disclose your findings. We'll endeavor to respond quickly, and will keep you updated throughout the process.

View File

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

View File

@ -3,6 +3,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe, json import frappe, json
from frappe import _
from frappe.utils import add_to_date, date_diff, getdate, nowdate, get_last_day, formatdate from frappe.utils import add_to_date, date_diff, getdate, nowdate, get_last_day, formatdate
from erpnext.accounts.report.general_ledger.general_ledger import execute from erpnext.accounts.report.general_ledger.general_ledger import execute
from frappe.core.page.dashboard.dashboard import cache_source, get_from_date_from_timespan from frappe.core.page.dashboard.dashboard import cache_source, get_from_date_from_timespan
@ -24,6 +25,9 @@ def get(chart_name = None, chart = None, no_cache = None, from_date = None, to_d
account = filters.get("account") account = filters.get("account")
company = filters.get("company") company = filters.get("company")
if not account and chart:
frappe.throw(_("Account is not set for the dashboard chart {0}").format(chart))
if not to_date: if not to_date:
to_date = nowdate() to_date = nowdate()
if not from_date: if not from_date:

View File

@ -117,7 +117,7 @@ class Account(NestedSet):
if not parent_acc_name_map: return if not parent_acc_name_map: return
self.create_account_for_child_company(parent_acc_name_map, descendants) self.create_account_for_child_company(parent_acc_name_map, descendants, parent_acc_name)
def validate_group_or_ledger(self): def validate_group_or_ledger(self):
if self.get("__islocal"): if self.get("__islocal"):
@ -159,7 +159,7 @@ class Account(NestedSet):
if frappe.db.get_value("GL Entry", {"account": self.name}): if frappe.db.get_value("GL Entry", {"account": self.name}):
frappe.throw(_("Currency can not be changed after making entries using some other currency")) frappe.throw(_("Currency can not be changed after making entries using some other currency"))
def create_account_for_child_company(self, parent_acc_name_map, descendants): def create_account_for_child_company(self, parent_acc_name_map, descendants, parent_acc_name):
for company in descendants: for company in descendants:
if not parent_acc_name_map.get(company): if not parent_acc_name_map.get(company):
frappe.throw(_("While creating account for child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA") frappe.throw(_("While creating account for child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA")

View File

@ -160,7 +160,7 @@ def _make_test_records(verbose):
["_Test Payable USD", "Current Liabilities", 0, "Payable", "USD"] ["_Test Payable USD", "Current Liabilities", 0, "Payable", "USD"]
] ]
for company, abbr in [["_Test Company", "_TC"], ["_Test Company 1", "_TC1"]]: for company, abbr in [["_Test Company", "_TC"], ["_Test Company 1", "_TC1"], ["_Test Company with perpetual inventory", "TCP1"]]:
test_objects = make_test_objects("Account", [{ test_objects = make_test_objects("Account", [{
"doctype": "Account", "doctype": "Account",
"account_name": account_name, "account_name": account_name,

View File

@ -2,6 +2,15 @@
// For license information, please see license.txt // For license information, please see license.txt
frappe.ui.form.on('Coupon Code', { frappe.ui.form.on('Coupon Code', {
setup: function(frm) {
frm.set_query("pricing_rule", function() {
return {
filters: [
["Pricing Rule","coupon_code_based", "=", "1"]
]
};
});
},
coupon_name:function(frm){ coupon_name:function(frm){
if (frm.doc.__islocal===1) { if (frm.doc.__islocal===1) {
frm.trigger("make_coupon_code"); frm.trigger("make_coupon_code");

View File

@ -24,6 +24,7 @@
], ],
"fields": [ "fields": [
{ {
"description": "e.g. \"Summer Holiday 2019 Offer 20\"",
"fieldname": "coupon_name", "fieldname": "coupon_name",
"fieldtype": "Data", "fieldtype": "Data",
"label": "Coupon Name", "label": "Coupon Name",
@ -50,7 +51,7 @@
"fieldtype": "Column Break" "fieldtype": "Column Break"
}, },
{ {
"description": "To be used to get discount", "description": "unique e.g. SAVE20 To be used to get discount",
"fieldname": "coupon_code", "fieldname": "coupon_code",
"fieldtype": "Data", "fieldtype": "Data",
"label": "Coupon Code", "label": "Coupon Code",
@ -62,12 +63,13 @@
"fieldname": "pricing_rule", "fieldname": "pricing_rule",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Pricing Rule", "label": "Pricing Rule",
"options": "Pricing Rule" "options": "Pricing Rule",
"reqd": 1
}, },
{ {
"fieldname": "uses", "fieldname": "uses",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Uses" "label": "Validity and Usage"
}, },
{ {
"fieldname": "valid_from", "fieldname": "valid_from",
@ -113,7 +115,7 @@
"read_only": 1 "read_only": 1
} }
], ],
"modified": "2019-10-15 14:12:22.686986", "modified": "2019-10-19 14:48:14.602481",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Coupon Code", "name": "Coupon Code",

View File

@ -8,10 +8,12 @@ import unittest
from frappe.utils import today, cint, flt, getdate from frappe.utils import today, cint, flt, getdate
from erpnext.accounts.doctype.loyalty_program.loyalty_program import get_loyalty_program_details_with_points from erpnext.accounts.doctype.loyalty_program.loyalty_program import get_loyalty_program_details_with_points
from erpnext.accounts.party import get_dashboard_info from erpnext.accounts.party import get_dashboard_info
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
class TestLoyaltyProgram(unittest.TestCase): class TestLoyaltyProgram(unittest.TestCase):
@classmethod @classmethod
def setUpClass(self): def setUpClass(self):
set_perpetual_inventory(0)
# create relevant item, customer, loyalty program, etc # create relevant item, customer, loyalty program, etc
create_records() create_records()

View File

@ -308,7 +308,7 @@ frappe.ui.form.on('Payment Entry', {
() => { () => {
frm.set_party_account_based_on_party = false; frm.set_party_account_based_on_party = false;
if (r.message.bank_account) { if (r.message.bank_account) {
frm.set_value("bank_account", r.message.bank_account); frm.set_value("party_bank_account", r.message.bank_account);
} }
} }
]); ]);

View File

@ -10,7 +10,7 @@ from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_ent
from frappe.utils import cint, flt, today, nowdate, add_days from frappe.utils import cint, flt, today, nowdate, add_days
import frappe.defaults import frappe.defaults
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \
test_records as pr_test_records test_records as pr_test_records, make_purchase_receipt, get_taxes
from erpnext.controllers.accounts_controller import get_payment_terms from erpnext.controllers.accounts_controller import get_payment_terms
from erpnext.exceptions import InvalidCurrency from erpnext.exceptions import InvalidCurrency
from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_transaction from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_transaction
@ -57,16 +57,11 @@ class TestPurchaseInvoice(unittest.TestCase):
self.assertEqual([d.debit, d.credit], expected_gl_entries.get(d.account)) self.assertEqual([d.debit, d.credit], expected_gl_entries.get(d.account))
def test_gl_entries_with_perpetual_inventory(self): def test_gl_entries_with_perpetual_inventory(self):
pi = frappe.copy_doc(test_records[1]) pi = make_purchase_invoice(company="_Test Company with perpetual inventory", supplier_warehouse="Work In Progress - TCP1", warehouse= "Stores - TCP1", cost_center = "Main - TCP1", expense_account ="_Test Account Cost for Goods Sold - TCP1", get_taxes_and_charges=True, qty=10)
set_perpetual_inventory(1, pi.company)
self.assertTrue(cint(erpnext.is_perpetual_inventory_enabled(pi.company)), 1) self.assertTrue(cint(erpnext.is_perpetual_inventory_enabled(pi.company)), 1)
pi.insert()
pi.submit()
self.check_gle_for_pi(pi.name) self.check_gle_for_pi(pi.name)
set_perpetual_inventory(0, pi.company)
def test_terms_added_after_save(self): def test_terms_added_after_save(self):
pi = frappe.copy_doc(test_records[1]) pi = frappe.copy_doc(test_records[1])
pi.insert() pi.insert()
@ -196,21 +191,21 @@ class TestPurchaseInvoice(unittest.TestCase):
self.assertEqual(pi.on_hold, 0) self.assertEqual(pi.on_hold, 0)
def test_gl_entries_with_perpetual_inventory_against_pr(self): def test_gl_entries_with_perpetual_inventory_against_pr(self):
pr = frappe.copy_doc(pr_test_records[0])
set_perpetual_inventory(1, pr.company)
self.assertTrue(cint(erpnext.is_perpetual_inventory_enabled(pr.company)), 1)
pr.submit()
pi = frappe.copy_doc(test_records[1]) pr = make_purchase_receipt(company="_Test Company with perpetual inventory", supplier_warehouse="Work In Progress - TCP1", warehouse= "Stores - TCP1", cost_center = "Main - TCP1", get_taxes_and_charges=True,)
for d in pi.get("items"):
self.assertTrue(cint(erpnext.is_perpetual_inventory_enabled(pr.company)), 1)
pi = make_purchase_invoice(company="_Test Company with perpetual inventory", supplier_warehouse="Work In Progress - TCP1", warehouse= "Stores - TCP1", cost_center = "Main - TCP1", expense_account ="_Test Account Cost for Goods Sold - TCP1", get_taxes_and_charges=True, qty=10,do_not_save= "True")
for d in pi.items:
d.purchase_receipt = pr.name d.purchase_receipt = pr.name
pi.insert() pi.insert()
pi.submit() pi.submit()
self.check_gle_for_pi(pi.name) self.check_gle_for_pi(pi.name)
set_perpetual_inventory(0, pr.company)
def check_gle_for_pi(self, pi): def check_gle_for_pi(self, pi):
gl_entries = frappe.db.sql("""select account, debit, credit gl_entries = frappe.db.sql("""select account, debit, credit
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
@ -218,10 +213,10 @@ class TestPurchaseInvoice(unittest.TestCase):
self.assertTrue(gl_entries) self.assertTrue(gl_entries)
expected_values = dict((d[0], d) for d in [ expected_values = dict((d[0], d) for d in [
["_Test Payable - _TC", 0, 720], ["Creditors - TCP1", 0, 720],
["Stock Received But Not Billed - _TC", 500.0, 0], ["Stock Received But Not Billed - TCP1", 500.0, 0],
["_Test Account Shipping Charges - _TC", 100.0, 0], ["_Test Account Shipping Charges - TCP1", 100.0, 0],
["_Test Account VAT - _TC", 120.0, 0], ["_Test Account VAT - TCP1", 120.0, 0],
]) ])
for i, gle in enumerate(gl_entries): for i, gle in enumerate(gl_entries):
@ -524,10 +519,9 @@ class TestPurchaseInvoice(unittest.TestCase):
self.assertFalse(gle) self.assertFalse(gle)
def test_purchase_invoice_update_stock_gl_entry_with_perpetual_inventory(self): def test_purchase_invoice_update_stock_gl_entry_with_perpetual_inventory(self):
set_perpetual_inventory()
pi = make_purchase_invoice(update_stock=1, posting_date=frappe.utils.nowdate(), pi = make_purchase_invoice(update_stock=1, posting_date=frappe.utils.nowdate(),
posting_time=frappe.utils.nowtime()) posting_time=frappe.utils.nowtime(), cash_bank_account="Cash - TCP1", company="_Test Company with perpetual inventory", supplier_warehouse="Work In Progress - TCP1", warehouse= "Stores - TCP1", cost_center = "Main - TCP1", expense_account ="_Test Account Cost for Goods Sold - TCP1")
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 debit_in_account_currency, credit_in_account_currency
@ -548,9 +542,9 @@ class TestPurchaseInvoice(unittest.TestCase):
self.assertEqual(expected_gl_entries[gle.account][2], gle.credit) self.assertEqual(expected_gl_entries[gle.account][2], gle.credit)
def test_purchase_invoice_for_is_paid_and_update_stock_gl_entry_with_perpetual_inventory(self): def test_purchase_invoice_for_is_paid_and_update_stock_gl_entry_with_perpetual_inventory(self):
set_perpetual_inventory()
pi = make_purchase_invoice(update_stock=1, posting_date=frappe.utils.nowdate(), pi = make_purchase_invoice(update_stock=1, posting_date=frappe.utils.nowdate(),
posting_time=frappe.utils.nowtime(), cash_bank_account="Cash - _TC", is_paid=1) posting_time=frappe.utils.nowtime(), cash_bank_account="Cash - TCP1", is_paid=1, company="_Test Company with perpetual inventory", supplier_warehouse="Work In Progress - TCP1", warehouse= "Stores - TCP1", cost_center = "Main - TCP1", expense_account ="_Test Account Cost for Goods Sold - TCP1")
gl_entries = frappe.db.sql("""select account, account_currency, sum(debit) as debit, gl_entries = frappe.db.sql("""select account, account_currency, sum(debit) as debit,
sum(credit) as credit, debit_in_account_currency, credit_in_account_currency sum(credit) as credit, debit_in_account_currency, credit_in_account_currency
@ -563,7 +557,7 @@ class TestPurchaseInvoice(unittest.TestCase):
expected_gl_entries = dict((d[0], d) for d in [ expected_gl_entries = dict((d[0], d) for d in [
[pi.credit_to, 250.0, 250.0], [pi.credit_to, 250.0, 250.0],
[stock_in_hand_account, 250.0, 0.0], [stock_in_hand_account, 250.0, 0.0],
["Cash - _TC", 0.0, 250.0] ["Cash - TCP1", 0.0, 250.0]
]) ])
for i, gle in enumerate(gl_entries): for i, gle in enumerate(gl_entries):
@ -630,6 +624,7 @@ class TestPurchaseInvoice(unittest.TestCase):
self.assertEqual(pi.get("items")[0].rm_supp_cost, flt(rm_supp_cost, 2)) self.assertEqual(pi.get("items")[0].rm_supp_cost, flt(rm_supp_cost, 2))
def test_rejected_serial_no(self): def test_rejected_serial_no(self):
set_perpetual_inventory(0)
pi = make_purchase_invoice(item_code="_Test Serialized Item With Series", received_qty=2, qty=1, pi = make_purchase_invoice(item_code="_Test Serialized Item With Series", received_qty=2, qty=1,
rejected_qty=1, rate=500, update_stock=1, rejected_qty=1, rate=500, update_stock=1,
rejected_warehouse = "_Test Rejected Warehouse - _TC") rejected_warehouse = "_Test Rejected Warehouse - _TC")
@ -881,7 +876,7 @@ def make_purchase_invoice(**args):
pi.is_return = args.is_return pi.is_return = args.is_return
pi.return_against = args.return_against pi.return_against = args.return_against
pi.is_subcontracted = args.is_subcontracted or "No" pi.is_subcontracted = args.is_subcontracted or "No"
pi.supplier_warehouse = "_Test Warehouse 1 - _TC" pi.supplier_warehouse = args.supplier_warehouse or "_Test Warehouse 1 - _TC"
pi.append("items", { pi.append("items", {
"item_code": args.item or args.item_code or "_Test Item", "item_code": args.item or args.item_code or "_Test Item",
@ -890,14 +885,21 @@ def make_purchase_invoice(**args):
"received_qty": args.received_qty or 0, "received_qty": args.received_qty or 0,
"rejected_qty": args.rejected_qty or 0, "rejected_qty": args.rejected_qty or 0,
"rate": args.rate or 50, "rate": args.rate or 50,
'expense_account': args.expense_account or '_Test Account Cost for Goods Sold - _TC',
"conversion_factor": 1.0, "conversion_factor": 1.0,
"serial_no": args.serial_no, "serial_no": args.serial_no,
"stock_uom": "_Test UOM", "stock_uom": "_Test UOM",
"cost_center": "_Test Cost Center - _TC", "cost_center": args.cost_center or "_Test Cost Center - _TC",
"project": args.project, "project": args.project,
"rejected_warehouse": args.rejected_warehouse or "", "rejected_warehouse": args.rejected_warehouse or "",
"rejected_serial_no": args.rejected_serial_no or "" "rejected_serial_no": args.rejected_serial_no or ""
}) })
if args.get_taxes_and_charges:
taxes = get_taxes()
for tax in taxes:
pi.append("taxes", tax)
if not args.do_not_save: if not args.do_not_save:
pi.insert() pi.insert()
if not args.do_not_submit: if not args.do_not_submit:

View File

@ -402,14 +402,21 @@ def make_invoice(doc_list={}, email_queue_list={}, customers_list={}):
for docs in doc_list: for docs in doc_list:
for name, doc in iteritems(docs): for name, doc in iteritems(docs):
if not frappe.db.exists('Sales Invoice', {'offline_pos_name': name}): if not frappe.db.exists('Sales Invoice', {'offline_pos_name': name}):
validate_records(doc) if isinstance(doc, dict):
si_doc = frappe.new_doc('Sales Invoice') validate_records(doc)
si_doc.offline_pos_name = name si_doc = frappe.new_doc('Sales Invoice')
si_doc.update(doc) si_doc.offline_pos_name = name
si_doc.set_posting_time = 1 si_doc.update(doc)
si_doc.customer = get_customer_id(doc) si_doc.set_posting_time = 1
si_doc.due_date = doc.get('posting_date') si_doc.customer = get_customer_id(doc)
name_list = submit_invoice(si_doc, name, doc, name_list) si_doc.due_date = doc.get('posting_date')
name_list = submit_invoice(si_doc, name, doc, name_list)
else:
doc.due_date = doc.get('posting_date')
doc.customer = get_customer_id(doc)
doc.set_posting_time = 1
doc.offline_pos_name = name
name_list = submit_invoice(doc, name, doc, name_list)
else: else:
name_list.append(name) name_list.append(name)

View File

@ -686,7 +686,6 @@ class SalesInvoice(SellingController):
def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False): def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False):
auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company) auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company)
if not gl_entries: if not gl_entries:
gl_entries = self.get_gl_entries() gl_entries = self.get_gl_entries()

View File

@ -68,8 +68,6 @@
"selling_price_list": "_Test Price List", "selling_price_list": "_Test Price List",
"territory": "_Test Territory" "territory": "_Test Territory"
}, },
{ {
"company": "_Test Company", "company": "_Test Company",
"conversion_rate": 1.0, "conversion_rate": 1.0,
@ -276,7 +274,6 @@
"uom": "_Test UOM 1", "uom": "_Test UOM 1",
"conversion_factor": 1, "conversion_factor": 1,
"stock_uom": "_Test UOM 1" "stock_uom": "_Test UOM 1"
}, },
{ {
"cost_center": "_Test Cost Center - _TC", "cost_center": "_Test Cost Center - _TC",

View File

@ -20,6 +20,9 @@ from erpnext.stock.doctype.item.test_item import create_item
from six import iteritems from six import iteritems
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction
from erpnext.regional.india.utils import get_ewb_data from erpnext.regional.india.utils import get_ewb_data
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
class TestSalesInvoice(unittest.TestCase): class TestSalesInvoice(unittest.TestCase):
def make(self): def make(self):
@ -550,7 +553,6 @@ class TestSalesInvoice(unittest.TestCase):
si.get("taxes")[6].tax_amount = 2 si.get("taxes")[6].tax_amount = 2
si.insert() si.insert()
print(si.name)
expected_values = [ expected_values = [
{ {
@ -679,56 +681,67 @@ class TestSalesInvoice(unittest.TestCase):
self.assertFalse(gle) self.assertFalse(gle)
def test_pos_gl_entry_with_perpetual_inventory(self): def test_pos_gl_entry_with_perpetual_inventory(self):
set_perpetual_inventory()
make_pos_profile() make_pos_profile()
self._insert_purchase_receipt() pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",supplier_warehouse= "Work In Progress - TCP1", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
pos = copy.deepcopy(test_records[1])
pos["is_pos"] = 1 pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True)
pos["update_stock"] = 1
pos["payments"] = [{'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 300}, pos.is_pos = 1
{'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300}] pos.update_stock = 1
pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - TCP1', 'amount': 50})
pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - TCP1', 'amount': 50})
taxes = get_taxes_and_charges()
pos.taxes = []
for tax in taxes:
pos.append("taxes", tax)
si = frappe.copy_doc(pos) si = frappe.copy_doc(pos)
si.insert() si.insert()
si.submit() si.submit()
self.assertEqual(si.paid_amount, 100.0)
self.assertEqual(si.paid_amount, 600.0) self.pos_gl_entry(si, pos, 50)
self.pos_gl_entry(si, pos, 300)
def test_pos_change_amount(self): def test_pos_change_amount(self):
set_perpetual_inventory()
make_pos_profile() make_pos_profile()
self._insert_purchase_receipt() pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",supplier_warehouse= "Work In Progress - TCP1", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
pos = copy.deepcopy(test_records[1])
pos["is_pos"] = 1
pos["update_stock"] = 1
pos["payments"] = [{'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 300},
{'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 340}]
si = frappe.copy_doc(pos) pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True)
si.change_amount = 5.0
si.insert()
si.submit()
self.assertEqual(si.grand_total, 630.0) pos.is_pos = 1
self.assertEqual(si.write_off_amount, -5) pos.update_stock = 1
pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - TCP1', 'amount': 50})
pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - TCP1', 'amount': 60})
pos.change_amount = 5.0
pos.insert()
pos.submit()
self.assertEqual(pos.grand_total, 100.0)
self.assertEqual(pos.write_off_amount, -5)
def test_make_pos_invoice(self): def test_make_pos_invoice(self):
from erpnext.accounts.doctype.sales_invoice.pos import make_invoice from erpnext.accounts.doctype.sales_invoice.pos import make_invoice
set_perpetual_inventory()
make_pos_profile() make_pos_profile()
self._insert_purchase_receipt() pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",supplier_warehouse= "Work In Progress - TCP1", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
pos = create_sales_invoice(company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1", do_not_save=True)
pos = copy.deepcopy(test_records[1]) pos.is_pos = 1
pos["is_pos"] = 1 pos.update_stock = 1
pos["update_stock"] = 1
pos["payments"] = [{'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 300}, pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - TCP1', 'amount': 50})
{'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 330}] pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - TCP1', 'amount': 50})
taxes = get_taxes_and_charges()
pos.taxes = []
for tax in taxes:
pos.append("taxes", tax)
invoice_data = [{'09052016142': pos}] invoice_data = [{'09052016142': pos}]
si = make_invoice(invoice_data).get('invoice') si = make_invoice(invoice_data).get('invoice')
@ -736,16 +749,15 @@ class TestSalesInvoice(unittest.TestCase):
sales_invoice = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': '09052016142', 'docstatus': 1}) sales_invoice = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': '09052016142', 'docstatus': 1})
si = frappe.get_doc('Sales Invoice', sales_invoice[0].name) si = frappe.get_doc('Sales Invoice', sales_invoice[0].name)
self.assertEqual(si.grand_total, 630.0)
self.pos_gl_entry(si, pos, 330) self.assertEqual(si.grand_total, 100)
self.pos_gl_entry(si, pos, 50)
def test_make_pos_invoice_in_draft(self): def test_make_pos_invoice_in_draft(self):
from erpnext.accounts.doctype.sales_invoice.pos import make_invoice from erpnext.accounts.doctype.sales_invoice.pos import make_invoice
from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.item.test_item import make_item
set_perpetual_inventory()
allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock') allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock')
if allow_negative_stock: if allow_negative_stock:
frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 0) frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 0)
@ -789,7 +801,7 @@ class TestSalesInvoice(unittest.TestCase):
si.name, as_dict=1)[0] si.name, as_dict=1)[0]
self.assertTrue(sle) self.assertTrue(sle)
self.assertEqual([sle.item_code, sle.warehouse, sle.actual_qty], self.assertEqual([sle.item_code, sle.warehouse, sle.actual_qty],
["_Test Item", "_Test Warehouse - _TC", -1.0]) ['_Test FG Item', 'Stores - TCP1', -1.0])
# check gl entries # check gl entries
gl_entries = frappe.db.sql("""select account, debit, credit gl_entries = frappe.db.sql("""select account, debit, credit
@ -797,19 +809,19 @@ class TestSalesInvoice(unittest.TestCase):
order by account asc, debit asc, credit asc""", si.name, as_dict=1) order by account asc, debit asc, credit asc""", si.name, as_dict=1)
self.assertTrue(gl_entries) self.assertTrue(gl_entries)
stock_in_hand = get_inventory_account('_Test Company') stock_in_hand = get_inventory_account('_Test Company with perpetual inventory')
expected_gl_entries = sorted([ expected_gl_entries = sorted([
[si.debit_to, 630.0, 0.0], [si.debit_to, 100.0, 0.0],
[pos["items"][0]["income_account"], 0.0, 500.0], [pos.items[0].income_account, 0.0, 89.09],
[pos["taxes"][0]["account_head"], 0.0, 80.0], ['Round Off - TCP1', 0.0, 0.01],
[pos["taxes"][1]["account_head"], 0.0, 50.0], [pos.taxes[0].account_head, 0.0, 10.69],
[pos.taxes[1].account_head, 0.0, 0.21],
[stock_in_hand, 0.0, abs(sle.stock_value_difference)], [stock_in_hand, 0.0, abs(sle.stock_value_difference)],
[pos["items"][0]["expense_account"], abs(sle.stock_value_difference), 0.0], [pos.items[0].expense_account, abs(sle.stock_value_difference), 0.0],
[si.debit_to, 0.0, 300.0], [si.debit_to, 0.0, 50.0],
[si.debit_to, 0.0, cash_amount], [si.debit_to, 0.0, cash_amount],
["_Test Bank - _TC", 300.0, 0.0], ["_Test Bank - TCP1", 50, 0.0],
["Cash - _TC", cash_amount, 0.0] ["Cash - TCP1", cash_amount, 0.0]
]) ])
for i, gle in enumerate(sorted(gl_entries, key=lambda gle: gle.account)): for i, gle in enumerate(sorted(gl_entries, key=lambda gle: gle.account)):
@ -823,9 +835,9 @@ class TestSalesInvoice(unittest.TestCase):
self.assertFalse(gle) self.assertFalse(gle)
set_perpetual_inventory(0)
frappe.db.sql("delete from `tabPOS Profile`") frappe.db.sql("delete from `tabPOS Profile`")
si.delete()
def test_pos_si_without_payment(self): def test_pos_si_without_payment(self):
set_perpetual_inventory() set_perpetual_inventory()
@ -1008,7 +1020,6 @@ class TestSalesInvoice(unittest.TestCase):
""" """
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
se = make_serialized_item() se = make_serialized_item()
@ -1023,14 +1034,17 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEqual(si.get("items")[0].serial_no, dn.get("items")[0].serial_no) self.assertEqual(si.get("items")[0].serial_no, dn.get("items")[0].serial_no)
def test_return_sales_invoice(self): def test_return_sales_invoice(self):
set_perpetual_inventory() make_stock_entry(item_code="_Test Item", target="Stores - TCP1", qty=50, basic_rate=100)
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, basic_rate=100)
actual_qty_0 = get_qty_after_transaction() actual_qty_0 = get_qty_after_transaction(item_code = "_Test Item", warehouse = "Stores - TCP1")
si = create_sales_invoice(qty=5, rate=500, update_stock=1) si = create_sales_invoice(qty = 5, rate=500, update_stock=1, company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1")
actual_qty_1 = get_qty_after_transaction(item_code = "_Test Item", warehouse = "Stores - TCP1")
frappe.db.commit()
actual_qty_1 = get_qty_after_transaction()
self.assertEqual(actual_qty_0 - 5, actual_qty_1) self.assertEqual(actual_qty_0 - 5, actual_qty_1)
# outgoing_rate # outgoing_rate
@ -1038,10 +1052,9 @@ class TestSalesInvoice(unittest.TestCase):
"voucher_no": si.name}, "stock_value_difference") / 5 "voucher_no": si.name}, "stock_value_difference") / 5
# return entry # return entry
si1 = create_sales_invoice(is_return=1, return_against=si.name, qty=-2, rate=500, update_stock=1) si1 = create_sales_invoice(is_return=1, return_against=si.name, qty=-2, rate=500, update_stock=1, company= "_Test Company with perpetual inventory", debit_to="Debtors - TCP1", item_code= "_Test Item", warehouse="Stores - TCP1", income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1", cost_center = "Main - TCP1")
actual_qty_2 = get_qty_after_transaction()
actual_qty_2 = get_qty_after_transaction(item_code = "_Test Item", warehouse = "Stores - TCP1")
self.assertEqual(actual_qty_1 + 2, actual_qty_2) self.assertEqual(actual_qty_1 + 2, actual_qty_2)
incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry", incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
@ -1049,7 +1062,7 @@ class TestSalesInvoice(unittest.TestCase):
["incoming_rate", "stock_value_difference"]) ["incoming_rate", "stock_value_difference"])
self.assertEqual(flt(incoming_rate, 3), abs(flt(outgoing_rate, 3))) self.assertEqual(flt(incoming_rate, 3), abs(flt(outgoing_rate, 3)))
stock_in_hand_account = get_inventory_account('_Test Company', si1.items[0].warehouse) stock_in_hand_account = get_inventory_account('_Test Company with perpetual inventory', si1.items[0].warehouse)
# Check gl entry # Check gl entry
gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Sales Invoice", gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Sales Invoice",
@ -1058,7 +1071,7 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEqual(gle_warehouse_amount, stock_value_difference) self.assertEqual(gle_warehouse_amount, stock_value_difference)
party_credited = frappe.db.get_value("GL Entry", {"voucher_type": "Sales Invoice", party_credited = frappe.db.get_value("GL Entry", {"voucher_type": "Sales Invoice",
"voucher_no": si1.name, "account": "Debtors - _TC", "party": "_Test Customer"}, "credit") "voucher_no": si1.name, "account": "Debtors - TCP1", "party": "_Test Customer"}, "credit")
self.assertEqual(party_credited, 1000) self.assertEqual(party_credited, 1000)
@ -1066,7 +1079,6 @@ class TestSalesInvoice(unittest.TestCase):
self.assertFalse(si1.outstanding_amount) self.assertFalse(si1.outstanding_amount)
self.assertEqual(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"), 1500) self.assertEqual(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"), 1500)
set_perpetual_inventory(0)
def test_discount_on_net_total(self): def test_discount_on_net_total(self):
si = frappe.copy_doc(test_records[2]) si = frappe.copy_doc(test_records[2])
@ -1524,6 +1536,8 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEqual(si.total_taxes_and_charges, 577.05) self.assertEqual(si.total_taxes_and_charges, 577.05)
self.assertEqual(si.grand_total, 1827.05) self.assertEqual(si.grand_total, 1827.05)
def test_create_invoice_without_terms(self): def test_create_invoice_without_terms(self):
si = create_sales_invoice(do_not_save=1) si = create_sales_invoice(do_not_save=1)
self.assertFalse(si.get('payment_schedule')) self.assertFalse(si.get('payment_schedule'))
@ -1930,4 +1944,29 @@ def get_outstanding_amount(against_voucher_type, against_voucher, account, party
if against_voucher_type == 'Purchase Invoice': if against_voucher_type == 'Purchase Invoice':
bal = bal * -1 bal = bal * -1
return bal return bal
def get_taxes_and_charges():
return [{
"account_head": "_Test Account Excise Duty - TCP1",
"charge_type": "On Net Total",
"cost_center": "Main - TCP1",
"description": "Excise Duty",
"doctype": "Sales Taxes and Charges",
"idx": 1,
"included_in_print_rate": 1,
"parentfield": "taxes",
"rate": 12
},
{
"account_head": "_Test Account Education Cess - TCP1",
"charge_type": "On Previous Row Amount",
"cost_center": "Main - TCP1",
"description": "Education Cess",
"doctype": "Sales Taxes and Charges",
"idx": 2,
"included_in_print_rate": 1,
"parentfield": "taxes",
"rate": 2,
"row_id": 1
}]

View File

@ -14,13 +14,13 @@ class TestShippingRule(unittest.TestCase):
shipping_rule.name = test_records[0].get('name') shipping_rule.name = test_records[0].get('name')
shipping_rule.get("conditions")[0].from_value = 101 shipping_rule.get("conditions")[0].from_value = 101
self.assertRaises(FromGreaterThanToError, shipping_rule.insert) self.assertRaises(FromGreaterThanToError, shipping_rule.insert)
def test_many_zero_to_values(self): def test_many_zero_to_values(self):
shipping_rule = frappe.copy_doc(test_records[0]) shipping_rule = frappe.copy_doc(test_records[0])
shipping_rule.name = test_records[0].get('name') shipping_rule.name = test_records[0].get('name')
shipping_rule.get("conditions")[0].to_value = 0 shipping_rule.get("conditions")[0].to_value = 0
self.assertRaises(ManyBlankToValuesError, shipping_rule.insert) self.assertRaises(ManyBlankToValuesError, shipping_rule.insert)
def test_overlapping_conditions(self): def test_overlapping_conditions(self):
for range_a, range_b in [ for range_a, range_b in [
((50, 150), (0, 100)), ((50, 150), (0, 100)),
@ -38,6 +38,10 @@ class TestShippingRule(unittest.TestCase):
self.assertRaises(OverlappingConditionError, shipping_rule.insert) self.assertRaises(OverlappingConditionError, shipping_rule.insert)
def create_shipping_rule(shipping_rule_type, shipping_rule_name): def create_shipping_rule(shipping_rule_type, shipping_rule_name):
if frappe.db.exists("Shipping Rule", shipping_rule_name):
return frappe.get_doc("Shipping Rule", shipping_rule_name)
sr = frappe.new_doc("Shipping Rule") sr = frappe.new_doc("Shipping Rule")
sr.account = "_Test Account Shipping Charges - _TC" sr.account = "_Test Account Shipping Charges - _TC"
sr.calculate_based_on = "Net Total" sr.calculate_based_on = "Net Total"
@ -70,4 +74,4 @@ def create_shipping_rule(shipping_rule_type, shipping_rule_name):
}) })
sr.insert(ignore_permissions=True) sr.insert(ignore_permissions=True)
sr.submit() sr.submit()
return sr return sr

View File

@ -3,8 +3,9 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe, erpnext import frappe, erpnext
from frappe.utils import flt, cstr, cint from frappe.utils import flt, cstr, cint, comma_and
from frappe import _ from frappe import _
from erpnext.accounts.utils import get_stock_and_account_balance
from frappe.model.meta import get_field_precision from frappe.model.meta import get_field_precision
from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
@ -12,6 +13,7 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import g
class ClosedAccountingPeriod(frappe.ValidationError): pass class ClosedAccountingPeriod(frappe.ValidationError): pass
class StockAccountInvalidTransaction(frappe.ValidationError): pass class StockAccountInvalidTransaction(frappe.ValidationError): pass
class StockValueAndAccountBalanceOutOfSync(frappe.ValidationError): pass
def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, update_outstanding='Yes', from_repost=False): def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, update_outstanding='Yes', from_repost=False):
if gl_map: if gl_map:
@ -115,11 +117,9 @@ def check_if_in_list(gle, gl_map, dimensions=None):
def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False): def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
if not from_repost: if not from_repost:
validate_account_for_perpetual_inventory(gl_map)
validate_cwip_accounts(gl_map) validate_cwip_accounts(gl_map)
round_off_debit_credit(gl_map) round_off_debit_credit(gl_map)
for entry in gl_map: for entry in gl_map:
make_entry(entry, adv_adj, update_outstanding, from_repost) make_entry(entry, adv_adj, update_outstanding, from_repost)
@ -127,6 +127,10 @@ def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
if not from_repost: if not from_repost:
validate_expense_against_budget(entry) validate_expense_against_budget(entry)
if not from_repost:
validate_account_for_perpetual_inventory(gl_map)
def make_entry(args, adv_adj, update_outstanding, from_repost=False): def make_entry(args, adv_adj, update_outstanding, from_repost=False):
args.update({"doctype": "GL Entry"}) args.update({"doctype": "GL Entry"})
gle = frappe.get_doc(args) gle = frappe.get_doc(args)
@ -137,15 +141,31 @@ def make_entry(args, adv_adj, update_outstanding, from_repost=False):
gle.submit() gle.submit()
def validate_account_for_perpetual_inventory(gl_map): def validate_account_for_perpetual_inventory(gl_map):
if cint(erpnext.is_perpetual_inventory_enabled(gl_map[0].company)) \ if cint(erpnext.is_perpetual_inventory_enabled(gl_map[0].company)):
and gl_map[0].voucher_type=="Journal Entry": account_list = [gl_entries.account for gl_entries in gl_map]
aii_accounts = [d[0] for d in frappe.db.sql("""select name from tabAccount
where account_type = 'Stock' and is_group=0""")]
for entry in gl_map: aii_accounts = [d.name for d in frappe.get_all("Account",
if entry.account in aii_accounts: filters={'account_type': 'Stock', 'is_group': 0, 'company': gl_map[0].company})]
for account in account_list:
if account not in aii_accounts:
continue
account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(account,
gl_map[0].posting_date, gl_map[0].company)
if gl_map[0].voucher_type=="Journal Entry":
# In case of Journal Entry, there are no corresponding SL entries,
# hence deducting currency amount
account_bal -= flt(gl_map[0].debit) - flt(gl_map[0].credit)
if account_bal == stock_bal:
frappe.throw(_("Account: {0} can only be updated via Stock Transactions") frappe.throw(_("Account: {0} can only be updated via Stock Transactions")
.format(entry.account), StockAccountInvalidTransaction) .format(account), StockAccountInvalidTransaction)
elif account_bal != stock_bal:
frappe.throw(_("Account Balance ({0}) and Stock Value ({1}) is out of sync for account {2} and linked warehouse ({3}). Please create adjustment Journal Entry for amount {4}.")
.format(account_bal, stock_bal, account, comma_and(warehouse_list), stock_bal - account_bal),
StockValueAndAccountBalanceOutOfSync)
def validate_cwip_accounts(gl_map): def validate_cwip_accounts(gl_map):
if not cint(frappe.db.get_value("Asset Settings", None, "disable_cwip_accounting")) \ if not cint(frappe.db.get_value("Asset Settings", None, "disable_cwip_accounting")) \

View File

@ -69,7 +69,7 @@ def get_columns(filters):
for year in fiscal_year: for year in fiscal_year:
for from_date, to_date in get_period_date_ranges(filters["period"], year[0]): for from_date, to_date in get_period_date_ranges(filters["period"], year[0]):
if filters["period"] == "Yearly": if filters["period"] == "Yearly":
labels = [_("Budget") + " " + str(year[0]), _("Actual ") + " " + str(year[0]), _("Varaiance ") + " " + str(year[0])] labels = [_("Budget") + " " + str(year[0]), _("Actual ") + " " + str(year[0]), _("Variance ") + " " + str(year[0])]
for label in labels: for label in labels:
columns.append(label+":Float:150") columns.append(label+":Float:150")
else: else:

View File

@ -13,6 +13,10 @@ from six import iteritems
# imported to enable erpnext.accounts.utils.get_account_currency # imported to enable erpnext.accounts.utils.get_account_currency
from erpnext.accounts.doctype.account.account import get_account_currency from erpnext.accounts.doctype.account.account import get_account_currency
from erpnext.stock.utils import get_stock_value_on
from erpnext.stock import get_warehouse_account_map
class FiscalYearError(frappe.ValidationError): pass class FiscalYearError(frappe.ValidationError): pass
@frappe.whitelist() @frappe.whitelist()
@ -560,23 +564,23 @@ def fix_total_debit_credit():
(dr_or_cr, dr_or_cr, '%s', '%s', '%s', dr_or_cr), (dr_or_cr, dr_or_cr, '%s', '%s', '%s', dr_or_cr),
(d.diff, d.voucher_type, d.voucher_no)) (d.diff, d.voucher_type, d.voucher_no))
def get_stock_and_account_difference(account_list=None, posting_date=None, company=None): def get_stock_and_account_balance(account=None, posting_date=None, company=None):
from erpnext.stock.utils import get_stock_value_on
from erpnext.stock import get_warehouse_account_map
if not posting_date: posting_date = nowdate() if not posting_date: posting_date = nowdate()
difference = {}
warehouse_account = get_warehouse_account_map(company) warehouse_account = get_warehouse_account_map(company)
for warehouse, account_data in iteritems(warehouse_account): account_balance = get_balance_on(account, posting_date, in_account_currency=False)
if account_data.get('account') in account_list:
account_balance = get_balance_on(account_data.get('account'), posting_date, in_account_currency=False)
stock_value = get_stock_value_on(warehouse, posting_date)
if abs(flt(stock_value) - flt(account_balance)) > 0.005:
difference.setdefault(account_data.get('account'), flt(stock_value) - flt(account_balance))
return difference related_warehouses = [wh for wh, wh_details in warehouse_account.items()
if wh_details.account == account and not wh_details.is_group]
total_stock_value = 0.0
for warehouse in related_warehouses:
value = get_stock_value_on(warehouse, posting_date)
total_stock_value += value
precision = frappe.get_precision("Journal Entry Account", "debit_in_account_currency")
return flt(account_balance, precision), flt(total_stock_value, precision), related_warehouses
def get_currency_precision(): def get_currency_precision():
precision = cint(frappe.db.get_default("currency_precision")) precision = cint(frappe.db.get_default("currency_precision"))

View File

@ -1,13 +1,13 @@
{ {
"add_total_row": 0, "add_total_row": 0,
"creation": "2019-09-23 16:35:02.836134", "creation": "2019-09-23 16:35:02.836134",
"disable_prepared_report": 0, "disable_prepared_report": 1,
"disabled": 0, "disabled": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "Report", "doctype": "Report",
"idx": 0, "idx": 0,
"is_standard": "Yes", "is_standard": "Yes",
"modified": "2019-09-23 16:35:02.836134", "modified": "2019-10-22 13:00:31.539726",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Assets", "module": "Assets",
"name": "Fixed Asset Register", "name": "Fixed Asset Register",

View File

@ -4,6 +4,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import cstr
def execute(filters=None): def execute(filters=None):
filters = frappe._dict(filters or {}) filters = frappe._dict(filters or {})
@ -149,12 +150,12 @@ def get_finance_book_value_map(finance_book=''):
FROM `tabAsset Finance Book` FROM `tabAsset Finance Book`
WHERE WHERE
parentfield='finance_books' parentfield='finance_books'
AND finance_book=%s''', (finance_book))) AND ifnull(finance_book, '')=%s''', cstr(finance_book)))
def get_purchase_receipt_supplier_map(): def get_purchase_receipt_supplier_map():
return frappe._dict(frappe.db.sql(''' Select return frappe._dict(frappe.db.sql(''' Select
pr.name, pr.supplier pr.name, pr.supplier
FROM `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pri FROM `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pri
WHERE WHERE
pri.parent = pr.name pri.parent = pr.name
AND pri.is_fixed_asset=1 AND pri.is_fixed_asset=1
@ -164,7 +165,7 @@ def get_purchase_receipt_supplier_map():
def get_purchase_invoice_supplier_map(): def get_purchase_invoice_supplier_map():
return frappe._dict(frappe.db.sql(''' Select return frappe._dict(frappe.db.sql(''' Select
pi.name, pi.supplier pi.name, pi.supplier
FROM `tabPurchase Invoice` pi, `tabPurchase Invoice Item` pii FROM `tabPurchase Invoice` pi, `tabPurchase Invoice Item` pii
WHERE WHERE
pii.parent = pi.name pii.parent = pi.name
AND pii.is_fixed_asset=1 AND pii.is_fixed_asset=1

View File

@ -18,7 +18,7 @@ def get_data():
"onboard_present": 1 "onboard_present": 1
}, },
{ {
"module_name": "Accounting", "module_name": "Accounts",
"category": "Modules", "category": "Modules",
"label": _("Accounting"), "label": _("Accounting"),
"color": "#3498db", "color": "#3498db",

View File

@ -127,7 +127,11 @@ def get_data():
"name": "Shipping Rule", "name": "Shipping Rule",
"description": _("Rules for adding shipping costs."), "description": _("Rules for adding shipping costs."),
}, },
{
"type": "doctype",
"name": "Coupon Code",
"description": _("Define coupon codes."),
}
] ]
}, },
{ {

View File

@ -1172,6 +1172,7 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna
def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"): def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"):
data = json.loads(trans_items) data = json.loads(trans_items)
sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation']
parent = frappe.get_doc(parent_doctype, parent_doctype_name) parent = frappe.get_doc(parent_doctype, parent_doctype_name)
for d in data: for d in data:
@ -1204,18 +1205,22 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
# if rate is greater than price_list_rate, set margin # if rate is greater than price_list_rate, set margin
# or set discount # or set discount
child_item.discount_percentage = 0 child_item.discount_percentage = 0
child_item.margin_type = "Amount"
child_item.margin_rate_or_amount = flt(child_item.rate - child_item.price_list_rate, if parent_doctype in sales_doctypes:
child_item.precision("margin_rate_or_amount")) child_item.margin_type = "Amount"
child_item.rate_with_margin = child_item.rate child_item.margin_rate_or_amount = flt(child_item.rate - child_item.price_list_rate,
child_item.precision("margin_rate_or_amount"))
child_item.rate_with_margin = child_item.rate
else: else:
child_item.discount_percentage = flt((1 - flt(child_item.rate) / flt(child_item.price_list_rate)) * 100.0, child_item.discount_percentage = flt((1 - flt(child_item.rate) / flt(child_item.price_list_rate)) * 100.0,
child_item.precision("discount_percentage")) child_item.precision("discount_percentage"))
child_item.discount_amount = flt( child_item.discount_amount = flt(
child_item.price_list_rate) - flt(child_item.rate) child_item.price_list_rate) - flt(child_item.rate)
child_item.margin_type = ""
child_item.margin_rate_or_amount = 0 if parent_doctype in sales_doctypes:
child_item.rate_with_margin = 0 child_item.margin_type = ""
child_item.margin_rate_or_amount = 0
child_item.rate_with_margin = 0
child_item.flags.ignore_validate_update_after_submit = True child_item.flags.ignore_validate_update_after_submit = True
if new_child_flag: if new_child_flag:

View File

@ -152,6 +152,20 @@ def tax_account_query(doctype, txt, searchfield, start, page_len, filters):
def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False): def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False):
conditions = [] conditions = []
#Get searchfields from meta and use in Item Link field query
meta = frappe.get_meta("Item", cached=True)
searchfields = meta.get_search_fields()
if "description" in searchfields:
searchfields.remove("description")
columns = [field for field in searchfields if not field in ["name", "item_group", "description"]]
columns = ", ".join(columns)
searchfields = searchfields + [field for field in[searchfield or "name", "item_code", "item_group", "item_name"]
if not field in searchfields]
searchfields = " or ".join([field + " like %(txt)s" for field in searchfields])
description_cond = '' description_cond = ''
if frappe.db.count('Item', cache=True) < 50000: if frappe.db.count('Item', cache=True) < 50000:
# scan description only if items are less than 50000 # scan description only if items are less than 50000
@ -162,17 +176,14 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
concat(substr(tabItem.item_name, 1, 40), "..."), item_name) as item_name, concat(substr(tabItem.item_name, 1, 40), "..."), item_name) as item_name,
tabItem.item_group, tabItem.item_group,
if(length(tabItem.description) > 40, \ if(length(tabItem.description) > 40, \
concat(substr(tabItem.description, 1, 40), "..."), description) as decription concat(substr(tabItem.description, 1, 40), "..."), description) as description,
{columns}
from tabItem from tabItem
where tabItem.docstatus < 2 where tabItem.docstatus < 2
and tabItem.has_variants=0 and tabItem.has_variants=0
and tabItem.disabled=0 and tabItem.disabled=0
and (tabItem.end_of_life > %(today)s or ifnull(tabItem.end_of_life, '0000-00-00')='0000-00-00') and (tabItem.end_of_life > %(today)s or ifnull(tabItem.end_of_life, '0000-00-00')='0000-00-00')
and (tabItem.`{key}` LIKE %(txt)s and ({scond} or tabItem.item_code IN (select parent from `tabItem Barcode` where barcode LIKE %(txt)s)
or tabItem.item_code LIKE %(txt)s
or tabItem.item_group LIKE %(txt)s
or tabItem.item_name LIKE %(txt)s
or tabItem.item_code IN (select parent from `tabItem Barcode` where barcode LIKE %(txt)s)
{description_cond}) {description_cond})
{fcond} {mcond} {fcond} {mcond}
order by order by
@ -182,6 +193,8 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
name, item_name name, item_name
limit %(start)s, %(page_len)s """.format( limit %(start)s, %(page_len)s """.format(
key=searchfield, key=searchfield,
columns=columns,
scond=searchfields,
fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'), fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'),
mcond=get_match_cond(doctype).replace('%', '%%'), mcond=get_match_cond(doctype).replace('%', '%%'),
description_cond = description_cond), description_cond = description_cond),
@ -280,22 +293,31 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
"page_len": page_len "page_len": page_len
} }
having_clause = "having sum(sle.actual_qty) > 0"
if filters.get("is_return"):
having_clause = ""
if args.get('warehouse'): if args.get('warehouse'):
batch_nos = frappe.db.sql("""select sle.batch_no, round(sum(sle.actual_qty),2), sle.stock_uom, concat('MFG-',batch.manufacturing_date), concat('EXP-',batch.expiry_date) batch_nos = frappe.db.sql("""select sle.batch_no, round(sum(sle.actual_qty),2), sle.stock_uom,
from `tabStock Ledger Entry` sle concat('MFG-',batch.manufacturing_date), concat('EXP-',batch.expiry_date)
INNER JOIN `tabBatch` batch on sle.batch_no = batch.name from `tabStock Ledger Entry` sle
where INNER JOIN `tabBatch` batch on sle.batch_no = batch.name
batch.disabled = 0 where
and sle.item_code = %(item_code)s batch.disabled = 0
and sle.warehouse = %(warehouse)s and sle.item_code = %(item_code)s
and (sle.batch_no like %(txt)s and sle.warehouse = %(warehouse)s
or batch.manufacturing_date like %(txt)s) and (sle.batch_no like %(txt)s
and batch.docstatus < 2 or batch.manufacturing_date like %(txt)s)
{0} and batch.docstatus < 2
{match_conditions} {cond}
group by batch_no having sum(sle.actual_qty) > 0 {match_conditions}
order by batch.expiry_date, sle.batch_no desc group by batch_no {having_clause}
limit %(start)s, %(page_len)s""".format(cond, match_conditions=get_match_cond(doctype)), args) order by batch.expiry_date, sle.batch_no desc
limit %(start)s, %(page_len)s""".format(
cond=cond,
match_conditions=get_match_cond(doctype),
having_clause = having_clause
), args)
return batch_nos return batch_nos
else: else:

View File

@ -207,41 +207,6 @@ class StockController(AccountsController):
reference_doctype=self.doctype, reference_doctype=self.doctype,
reference_name=self.name)).insert().name reference_name=self.name)).insert().name
def make_adjustment_entry(self, expected_gle, voucher_obj):
from erpnext.accounts.utils import get_stock_and_account_difference
account_list = [d.account for d in expected_gle]
acc_diff = get_stock_and_account_difference(account_list,
expected_gle[0].posting_date, self.company)
cost_center = self.get_company_default("cost_center")
stock_adjustment_account = self.get_company_default("stock_adjustment_account")
gl_entries = []
for account, diff in acc_diff.items():
if diff:
gl_entries.append([
# stock in hand account
voucher_obj.get_gl_dict({
"account": account,
"against": stock_adjustment_account,
"debit": diff,
"remarks": "Adjustment Accounting Entry for Stock",
}),
# account against stock in hand
voucher_obj.get_gl_dict({
"account": stock_adjustment_account,
"against": account,
"credit": diff,
"cost_center": cost_center or None,
"remarks": "Adjustment Accounting Entry for Stock",
}),
])
if gl_entries:
from erpnext.accounts.general_ledger import make_gl_entries
make_gl_entries(gl_entries)
def check_expense_account(self, item): def check_expense_account(self, item):
if not item.get("expense_account"): if not item.get("expense_account"):
frappe.throw(_("Expense or Difference account is mandatory for Item {0} as it impacts overall stock value").format(item.item_code)) frappe.throw(_("Expense or Difference account is mandatory for Item {0} as it impacts overall stock value").format(item.item_code))

View File

@ -10,13 +10,14 @@ from erpnext.demo.domains import data
from frappe import _ from frappe import _
def setup(domain): def setup(domain):
frappe.flags.in_demo = 1
complete_setup(domain) complete_setup(domain)
setup_demo_page() setup_demo_page()
setup_fiscal_year() setup_fiscal_year()
setup_holiday_list() setup_holiday_list()
setup_user() setup_user()
setup_employee() setup_employee()
setup_user_roles() setup_user_roles(domain)
setup_role_permissions() setup_role_permissions()
setup_custom_field_for_domain() setup_custom_field_for_domain()
@ -183,13 +184,19 @@ def setup_salary_structure(employees, salary_slip_based_on_timesheet=0):
return ss return ss
def setup_user_roles(): def setup_user_roles(domain):
user = frappe.get_doc('User', 'demo@erpnext.com') user = frappe.get_doc('User', 'demo@erpnext.com')
user.add_roles('HR User', 'HR Manager', 'Accounts User', 'Accounts Manager', user.add_roles('HR User', 'HR Manager', 'Accounts User', 'Accounts Manager',
'Stock User', 'Stock Manager', 'Sales User', 'Sales Manager', 'Purchase User', 'Stock User', 'Stock Manager', 'Sales User', 'Sales Manager', 'Purchase User',
'Purchase Manager', 'Projects User', 'Manufacturing User', 'Manufacturing Manager', 'Purchase Manager', 'Projects User', 'Manufacturing User', 'Manufacturing Manager',
'Support Team', 'Academics User', 'Physician', 'Healthcare Administrator', 'Laboratory User', 'Support Team')
'Nursing User', 'Patient')
if domain == "Healthcare":
user.add_roles('Physician', 'Healthcare Administrator', 'Laboratory User',
'Nursing User', 'Patient')
if domain == "Education":
user.add_roles('Academics User')
if not frappe.db.get_global('demo_hr_user'): if not frappe.db.get_global('demo_hr_user'):
user = frappe.get_doc('User', 'CaitlinSnow@example.com') user = frappe.get_doc('User', 'CaitlinSnow@example.com')
@ -219,7 +226,7 @@ def setup_user_roles():
if not frappe.db.get_global('demo_manufacturing_user'): if not frappe.db.get_global('demo_manufacturing_user'):
user = frappe.get_doc('User', 'NeptuniaAquaria@example.com') user = frappe.get_doc('User', 'NeptuniaAquaria@example.com')
user.add_roles('Manufacturing User', 'Stock User', 'Purchase User', 'Accounts User') user.add_roles('Manufacturing User', 'Stock Manager', 'Stock User', 'Purchase User', 'Accounts User')
update_employee_department(user.name, 'Production') update_employee_department(user.name, 'Production')
frappe.db.set_global('demo_manufacturing_user', user.name) frappe.db.set_global('demo_manufacturing_user', user.name)
@ -241,11 +248,12 @@ def setup_user_roles():
update_employee_department(user.name, 'Management') update_employee_department(user.name, 'Management')
frappe.db.set_global('demo_projects_user', user.name) frappe.db.set_global('demo_projects_user', user.name)
if not frappe.db.get_global('demo_education_user'): if domain == "Education":
user = frappe.get_doc('User', 'ArthurCurry@example.com') if not frappe.db.get_global('demo_education_user'):
user.add_roles('Academics User') user = frappe.get_doc('User', 'ArthurCurry@example.com')
update_employee_department(user.name, 'Management') user.add_roles('Academics User')
frappe.db.set_global('demo_education_user', user.name) update_employee_department(user.name, 'Management')
frappe.db.set_global('demo_education_user', user.name)
#Add Expense Approver #Add Expense Approver
user = frappe.get_doc('User', 'ClarkKent@example.com') user = frappe.get_doc('User', 'ClarkKent@example.com')

View File

@ -73,14 +73,16 @@ def work():
make_pos_invoice() make_pos_invoice()
def make_payment_entries(ref_doctype, report): def make_payment_entries(ref_doctype, report):
outstanding_invoices = list(set([r[3] for r in query_report.run(report, {
"report_date": frappe.flags.current_date, outstanding_invoices = frappe.get_all(ref_doctype, fields=["name"],
"company": erpnext.get_default_company() filters={
})["result"] if r[2]==ref_doctype])) "company": erpnext.get_default_company(),
"outstanding_amount": (">", 0.0)
})
# make Payment Entry # make Payment Entry
for inv in outstanding_invoices[:random.randint(1, 2)]: for inv in outstanding_invoices[:random.randint(1, 2)]:
pe = get_payment_entry(ref_doctype, inv) pe = get_payment_entry(ref_doctype, inv.name)
pe.posting_date = frappe.flags.current_date pe.posting_date = frappe.flags.current_date
pe.reference_no = random_string(6) pe.reference_no = random_string(6)
pe.reference_date = frappe.flags.current_date pe.reference_date = frappe.flags.current_date
@ -91,7 +93,7 @@ def make_payment_entries(ref_doctype, report):
# make payment via JV # make payment via JV
for inv in outstanding_invoices[:1]: for inv in outstanding_invoices[:1]:
jv = frappe.get_doc(get_payment_entry_against_invoice(ref_doctype, inv)) jv = frappe.get_doc(get_payment_entry_against_invoice(ref_doctype, inv.name))
jv.posting_date = frappe.flags.current_date jv.posting_date = frappe.flags.current_date
jv.cheque_no = random_string(6) jv.cheque_no = random_string(6)
jv.cheque_date = frappe.flags.current_date jv.cheque_date = frappe.flags.current_date

View File

@ -39,61 +39,4 @@ def make_project(current_date):
"doctype": "Project", "doctype": "Project",
"project_name": "New Product Development " + current_date.strftime("%Y-%m-%d"), "project_name": "New Product Development " + current_date.strftime("%Y-%m-%d"),
}) })
project.set("tasks", [
{
"title": "Review Requirements",
"start_date": frappe.utils.add_days(current_date, 10),
"end_date": frappe.utils.add_days(current_date, 11)
},
{
"title": "Design Options",
"start_date": frappe.utils.add_days(current_date, 11),
"end_date": frappe.utils.add_days(current_date, 20)
},
{
"title": "Make Prototypes",
"start_date": frappe.utils.add_days(current_date, 20),
"end_date": frappe.utils.add_days(current_date, 30)
},
{
"title": "Customer Feedback on Prototypes",
"start_date": frappe.utils.add_days(current_date, 30),
"end_date": frappe.utils.add_days(current_date, 40)
},
{
"title": "Freeze Feature Set",
"start_date": frappe.utils.add_days(current_date, 40),
"end_date": frappe.utils.add_days(current_date, 45)
},
{
"title": "Testing",
"start_date": frappe.utils.add_days(current_date, 45),
"end_date": frappe.utils.add_days(current_date, 60)
},
{
"title": "Product Engineering",
"start_date": frappe.utils.add_days(current_date, 45),
"end_date": frappe.utils.add_days(current_date, 55)
},
{
"title": "Supplier Contracts",
"start_date": frappe.utils.add_days(current_date, 55),
"end_date": frappe.utils.add_days(current_date, 70)
},
{
"title": "Design and Build Fixtures",
"start_date": frappe.utils.add_days(current_date, 45),
"end_date": frappe.utils.add_days(current_date, 65)
},
{
"title": "Test Run",
"start_date": frappe.utils.add_days(current_date, 70),
"end_date": frappe.utils.add_days(current_date, 80)
},
{
"title": "Launch",
"start_date": frappe.utils.add_days(current_date, 80),
"end_date": frappe.utils.add_days(current_date, 90)
},
])
project.insert() project.insert()

View File

@ -66,7 +66,7 @@ def make_opportunity(domain):
b = frappe.get_doc({ b = frappe.get_doc({
"doctype": "Opportunity", "doctype": "Opportunity",
"opportunity_from": "Customer", "opportunity_from": "Customer",
"customer": get_random("Customer"), "party_name": frappe.get_value("Customer", get_random("Customer"), 'name'),
"opportunity_type": "Sales", "opportunity_type": "Sales",
"with_items": 1, "with_items": 1,
"transaction_date": frappe.flags.current_date, "transaction_date": frappe.flags.current_date,

View File

@ -27,3 +27,16 @@ frappe.ui.form.on('Student', {
} }
} }
}); });
frappe.ui.form.on('Student Guardian', {
guardians_add: function(frm){
frm.fields_dict['guardians'].grid.get_field('guardian').get_query = function(doc){
var guardian_list = [];
if(!doc.__islocal) guardian_list.push(doc.guardian);
$.each(doc.guardians, function(idx, val){
if (val.guardian) guardian_list.push(val.guardian);
});
return { filters: [['Guardian', 'name', 'not in', guardian_list]] };
};
}
});

View File

@ -29,7 +29,8 @@ def sync_sales_order(order, request_id=None):
validate_item(order, shopify_settings) validate_item(order, shopify_settings)
create_order(order, shopify_settings) create_order(order, shopify_settings)
except Exception as e: except Exception as e:
make_shopify_log(status="Error", message=e.message, exception=False) make_shopify_log(status="Error", exception=e)
else: else:
make_shopify_log(status="Success") make_shopify_log(status="Success")
@ -42,9 +43,9 @@ def prepare_sales_invoice(order, request_id=None):
sales_order = get_sales_order(cstr(order['id'])) sales_order = get_sales_order(cstr(order['id']))
if sales_order: if sales_order:
create_sales_invoice(order, shopify_settings, sales_order) create_sales_invoice(order, shopify_settings, sales_order)
make_shopify_log(status="Success") make_shopify_log(status="Success")
except Exception: except Exception as e:
make_shopify_log(status="Error", exception=True) make_shopify_log(status="Error", exception=e, rollback=True)
def prepare_delivery_note(order, request_id=None): def prepare_delivery_note(order, request_id=None):
frappe.set_user('Administrator') frappe.set_user('Administrator')
@ -56,8 +57,8 @@ def prepare_delivery_note(order, request_id=None):
if sales_order: if sales_order:
create_delivery_note(order, shopify_settings, sales_order) create_delivery_note(order, shopify_settings, sales_order)
make_shopify_log(status="Success") make_shopify_log(status="Success")
except Exception: except Exception as e:
make_shopify_log(status="Error", exception=True) make_shopify_log(status="Error", exception=e, rollback=True)
def get_sales_order(shopify_order_id): def get_sales_order(shopify_order_id):
sales_order = frappe.db.get_value("Sales Order", filters={"shopify_order_id": shopify_order_id}) sales_order = frappe.db.get_value("Sales Order", filters={"shopify_order_id": shopify_order_id})
@ -97,7 +98,7 @@ def create_sales_order(shopify_order, shopify_settings, company=None):
message = 'Following items are exists in order but relevant record not found in Product master' message = 'Following items are exists in order but relevant record not found in Product master'
message += "\n" + ", ".join(product_not_exists) message += "\n" + ", ".join(product_not_exists)
make_shopify_log(status="Error", message=message, exception=True) make_shopify_log(status="Error", exception=e, rollback=True)
return '' return ''

View File

@ -12,23 +12,38 @@ class ShopifyLog(Document):
pass pass
def make_shopify_log(status="Queued", message=None, exception=False): def make_shopify_log(status="Queued", exception=None, rollback=False):
# if name not provided by log calling method then fetch existing queued state log # if name not provided by log calling method then fetch existing queued state log
make_new = False
if not frappe.flags.request_id: if not frappe.flags.request_id:
return make_new = True
log = frappe.get_doc("Shopify Log", frappe.flags.request_id) if rollback:
if exception:
frappe.db.rollback() frappe.db.rollback()
log = frappe.get_doc({"doctype":"Shopify Log"}).insert(ignore_permissions=True)
log.message = message if message else '' if make_new:
log = frappe.get_doc({"doctype":"Shopify Log"}).insert(ignore_permissions=True)
else:
log = log = frappe.get_doc("Shopify Log", frappe.flags.request_id)
log.message = get_message(exception)
log.traceback = frappe.get_traceback() log.traceback = frappe.get_traceback()
log.status = status log.status = status
log.save(ignore_permissions=True) log.save(ignore_permissions=True)
frappe.db.commit() frappe.db.commit()
def get_message(exception):
message = None
if hasattr(exception, 'message'):
message = exception.message
elif hasattr(exception, '__str__'):
message = exception.__str__()
else:
message = "Something went wrong while syncing"
return message
def dump_request_data(data, event="create/order"): def dump_request_data(data, event="create/order"):
event_mapper = { event_mapper = {
"orders/create": get_webhook_address(connector_name='shopify_connection', method="sync_sales_order", exclude_uri=True), "orders/create": get_webhook_address(connector_name='shopify_connection', method="sync_sales_order", exclude_uri=True),
@ -43,11 +58,11 @@ def dump_request_data(data, event="create/order"):
}).insert(ignore_permissions=True) }).insert(ignore_permissions=True)
frappe.db.commit() frappe.db.commit()
frappe.enqueue(method=event_mapper[event], queue='short', timeout=300, is_async=True, frappe.enqueue(method=event_mapper[event], queue='short', timeout=300, is_async=True,
**{"order": data, "request_id": log.name}) **{"order": data, "request_id": log.name})
@frappe.whitelist() @frappe.whitelist()
def resync(method, name, request_data): def resync(method, name, request_data):
frappe.db.set_value("Shopify Log", name, "status", "Queued", update_modified=False) frappe.db.set_value("Shopify Log", name, "status", "Queued", update_modified=False)
frappe.enqueue(method=method, queue='short', timeout=300, is_async=True, frappe.enqueue(method=method, queue='short', timeout=300, is_async=True,
**{"order": json.loads(request_data), "request_id": name}) **{"order": json.loads(request_data), "request_id": name})

View File

@ -30,13 +30,9 @@ class ShopifySettings(Document):
# url = get_shopify_url('admin/webhooks.json', self) # url = get_shopify_url('admin/webhooks.json', self)
created_webhooks = [d.method for d in self.webhooks] created_webhooks = [d.method for d in self.webhooks]
url = get_shopify_url('admin/api/2019-04/webhooks.json', self) url = get_shopify_url('admin/api/2019-04/webhooks.json', self)
print('url', url)
for method in webhooks: for method in webhooks:
print('method', method)
session = get_request_session() session = get_request_session()
print('session', session)
try: try:
print(get_header(self))
d = session.post(url, data=json.dumps({ d = session.post(url, data=json.dumps({
"webhook": { "webhook": {
"topic": method, "topic": method,
@ -44,11 +40,10 @@ class ShopifySettings(Document):
"format": "json" "format": "json"
} }
}), headers=get_header(self)) }), headers=get_header(self))
print('d', d.json())
d.raise_for_status() d.raise_for_status()
self.update_webhook_table(method, d.json()) self.update_webhook_table(method, d.json())
except Exception as e: except Exception as e:
make_shopify_log(status="Warning", message=e, exception=False) make_shopify_log(status="Warning", exception=e, rollback=True)
def unregister_webhooks(self): def unregister_webhooks(self):
session = get_request_session() session = get_request_session()
@ -67,7 +62,6 @@ class ShopifySettings(Document):
self.remove(d) self.remove(d)
def update_webhook_table(self, method, res): def update_webhook_table(self, method, res):
print('update')
self.append("webhooks", { self.append("webhooks", {
"webhook_id": res['webhook']['id'], "webhook_id": res['webhook']['id'],
"method": method "method": method
@ -75,7 +69,6 @@ class ShopifySettings(Document):
def get_shopify_url(path, settings): def get_shopify_url(path, settings):
if settings.app_type == "Private": if settings.app_type == "Private":
print(settings.api_key, settings.get_password('password'), settings.shopify_url, path)
return 'https://{}:{}@{}/{}'.format(settings.api_key, settings.get_password('password'), settings.shopify_url, path) return 'https://{}:{}@{}/{}'.format(settings.api_key, settings.get_password('password'), settings.shopify_url, path)
else: else:
return 'https://{}/{}'.format(settings.shopify_url, path) return 'https://{}/{}'.format(settings.shopify_url, path)

View File

@ -283,7 +283,6 @@ scheduler_events = {
], ],
"daily": [ "daily": [
"erpnext.stock.reorder_item.reorder_item", "erpnext.stock.reorder_item.reorder_item",
"erpnext.setup.doctype.email_digest.email_digest.send",
"erpnext.support.doctype.issue.issue.auto_close_tickets", "erpnext.support.doctype.issue.issue.auto_close_tickets",
"erpnext.crm.doctype.opportunity.opportunity.auto_close_opportunity", "erpnext.crm.doctype.opportunity.opportunity.auto_close_opportunity",
"erpnext.controllers.accounts_controller.update_invoice_status", "erpnext.controllers.accounts_controller.update_invoice_status",
@ -306,6 +305,7 @@ scheduler_events = {
"erpnext.crm.doctype.email_campaign.email_campaign.set_email_campaign_status" "erpnext.crm.doctype.email_campaign.email_campaign.set_email_campaign_status"
], ],
"daily_long": [ "daily_long": [
"erpnext.setup.doctype.email_digest.email_digest.send",
"erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_latest_price_in_all_boms", "erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_latest_price_in_all_boms",
"erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry.process_expired_allocation", "erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry.process_expired_allocation",
"erpnext.hr.utils.generate_leave_encashment" "erpnext.hr.utils.generate_leave_encashment"

View File

@ -2,11 +2,11 @@ frappe.listview_settings['Expense Claim'] = {
add_fields: ["total_claimed_amount", "docstatus"], add_fields: ["total_claimed_amount", "docstatus"],
get_indicator: function(doc) { get_indicator: function(doc) {
if(doc.status == "Paid") { if(doc.status == "Paid") {
return [__("Paid"), "green", "status,=,'Paid'"]; return [__("Paid"), "green", "status,=,Paid"];
}else if(doc.status == "Unpaid") { }else if(doc.status == "Unpaid") {
return [__("Unpaid"), "orange"]; return [__("Unpaid"), "orange", "status,=,Unpaid"];
} else if(doc.status == "Rejected") { } else if(doc.status == "Rejected") {
return [__("Rejected"), "grey"]; return [__("Rejected"), "grey", "status,=,Rejected"];
} }
} }
}; };

View File

@ -125,7 +125,7 @@ class LeaveApplication(Document):
status = "Half Day" if date == self.half_day_date else "On Leave" status = "Half Day" if date == self.half_day_date else "On Leave"
attendance_name = frappe.db.exists('Attendance', dict(employee = self.employee, attendance_name = frappe.db.exists('Attendance', dict(employee = self.employee,
attenance_date = date, docstatus = ('!=', 2))) attendance_date = date, docstatus = ('!=', 2)))
if attendance_name: if attendance_name:
# update existing attendance, change absent to on leave # update existing attendance, change absent to on leave
@ -503,14 +503,17 @@ def get_leave_allocation_records(employee, date, leave_type=None):
def get_pending_leaves_for_period(employee, leave_type, from_date, to_date): def get_pending_leaves_for_period(employee, leave_type, from_date, to_date):
''' Returns leaves that are pending approval ''' ''' Returns leaves that are pending approval '''
return frappe.db.get_value("Leave Application", leaves = frappe.get_all("Leave Application",
filters={ filters={
"employee": employee, "employee": employee,
"leave_type": leave_type, "leave_type": leave_type,
"from_date": ("<=", from_date),
"to_date": (">=", to_date),
"status": "Open" "status": "Open"
}, fieldname=['SUM(total_leave_days)']) or flt(0) },
or_filters={
"from_date": ["between", (from_date, to_date)],
"to_date": ["between", (from_date, to_date)]
}, fields=['SUM(total_leave_days) as leaves'])[0]
return leaves['leaves'] if leaves['leaves'] else 0.0
def get_remaining_leaves(allocation, leaves_taken, date, expiry): def get_remaining_leaves(allocation, leaves_taken, date, expiry):
''' Returns minimum leaves remaining after comparing with remaining days for allocation expiry ''' ''' Returns minimum leaves remaining after comparing with remaining days for allocation expiry '''

View File

@ -5,6 +5,12 @@ from frappe import _
def get_data(): def get_data():
return { return {
'fieldname': 'leave_application',
'transactions': [
{
'items': ['Attendance']
}
],
'reports': [ 'reports': [
{ {
'label': _('Reports'), 'label': _('Reports'),

View File

@ -72,7 +72,7 @@ class TestLeaveApplication(unittest.TestCase):
application.to_date = "2013-01-05" application.to_date = "2013-01-05"
return application return application
def test_attendance_creation(self): def test_overwrite_attendance(self):
'''check attendance is automatically created on leave approval''' '''check attendance is automatically created on leave approval'''
make_allocation_record() make_allocation_record()
application = self.get_application(_test_records[0]) application = self.get_application(_test_records[0])
@ -82,7 +82,8 @@ class TestLeaveApplication(unittest.TestCase):
application.insert() application.insert()
application.submit() application.submit()
attendance = frappe.get_all('Attendance', ['name', 'status', 'attendance_date'], dict(leave_application = application.name)) attendance = frappe.get_all('Attendance', ['name', 'status', 'attendance_date'],
dict(attendance_date=('between', ['2018-01-01', '2018-01-03']), docstatus=("!=", 2)))
# attendance created for all 3 days # attendance created for all 3 days
self.assertEqual(len(attendance), 3) self.assertEqual(len(attendance), 3)
@ -95,20 +96,6 @@ class TestLeaveApplication(unittest.TestCase):
for d in ('2018-01-01', '2018-01-02', '2018-01-03'): for d in ('2018-01-01', '2018-01-02', '2018-01-03'):
self.assertTrue(getdate(d) in dates) self.assertTrue(getdate(d) in dates)
def test_overwrite_attendance(self):
# employee marked as absent
doc = frappe.new_doc("Attendance")
doc.employee = '_T-Employee-00001'
doc.attendance_date = '2018-01-01'
doc.company = '_Test Company'
doc.status = 'Absent'
doc.flags.ignore_validate = True
doc.insert(ignore_permissions=True)
doc.submit()
# now check if the status has been updated
self.test_attendance_creation()
def test_block_list(self): def test_block_list(self):
self._clear_roles() self._clear_roles()

View File

@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe, erpnext import frappe, erpnext
import datetime, math import datetime, math
from frappe.utils import add_days, cint, cstr, flt, getdate, rounded, date_diff, money_in_words, getdate from frappe.utils import add_days, cint, cstr, flt, getdate, rounded, date_diff, money_in_words
from frappe.model.naming import make_autoname from frappe.model.naming import make_autoname
from frappe import msgprint, _ from frappe import msgprint, _

View File

@ -75,7 +75,7 @@ def get_data(filters):
leave_approvers = department_approver_map.get(employee.department_name, []).append(employee.leave_approver) leave_approvers = department_approver_map.get(employee.department_name, []).append(employee.leave_approver)
if (len(leave_approvers) and user in leave_approvers) or (user in ["Administrator", employee.user_id]) \ if (leave_approvers and len(leave_approvers) and user in leave_approvers) or (user in ["Administrator", employee.user_id]) \
or ("HR Manager" in frappe.get_roles(user)): or ("HR Manager" in frappe.get_roles(user)):
row = frappe._dict({ row = frappe._dict({
'employee': employee.name, 'employee': employee.name,
@ -111,10 +111,10 @@ def get_conditions(filters):
def get_department_leave_approver_map(department=None): def get_department_leave_approver_map(department=None):
conditions='' conditions=''
if department: if department:
conditions='and department_name = %(department)s or parent_department = %(department)s'%{'department': department} conditions="and (department_name = '%(department)s' or parent_department = '%(department)s')"%{'department': department}
# get current department and all its child # get current department and all its child
department_list = frappe.db.sql_list(''' SELECT name FROM `tabDepartment` WHERE disabled=0 {0}'''.format(conditions)) #nosec department_list = frappe.db.sql_list(""" SELECT name FROM `tabDepartment` WHERE disabled=0 {0}""".format(conditions)) #nosec
# retrieve approvers list from current department and from its subsequent child departments # retrieve approvers list from current department and from its subsequent child departments
approver_list = frappe.get_all('Department Approver', filters={ approver_list = frappe.get_all('Department Approver', filters={

View File

@ -20,6 +20,12 @@ frappe.ui.form.on('Maintenance Schedule', {
frm.set_value({transaction_date: frappe.datetime.get_today()}); frm.set_value({transaction_date: frappe.datetime.get_today()});
} }
}, },
refresh: function(frm) {
setTimeout(() => {
frm.toggle_display('generate_schedule', !(frm.is_new()));
frm.toggle_display('schedule', !(frm.is_new()));
},10);
},
customer: function(frm) { customer: function(frm) {
erpnext.utils.get_party_details(frm) erpnext.utils.get_party_details(frm)
}, },

View File

@ -150,7 +150,7 @@ class MaintenanceSchedule(TransactionBase):
elif not d.no_of_visits: elif not d.no_of_visits:
throw(_("Please mention no of visits required")) throw(_("Please mention no of visits required"))
elif not d.sales_person: elif not d.sales_person:
throw(_("Please select Incharge Person's name")) throw(_("Please select a Sales Person for item: {0}".format(d.item_name)))
if getdate(d.start_date) >= getdate(d.end_date): if getdate(d.start_date) >= getdate(d.end_date):
throw(_("Start date should be less than end date for Item {0}").format(d.item_code)) throw(_("Start date should be less than end date for Item {0}").format(d.item_code))

View File

@ -12,6 +12,7 @@ frappe.ui.form.on('Blanket Order', {
}, },
refresh: function(frm) { refresh: function(frm) {
erpnext.hide_company();
if (frm.doc.customer && frm.doc.docstatus === 1) { if (frm.doc.customer && frm.doc.docstatus === 1) {
frm.add_custom_button(__('View Orders'), function() { frm.add_custom_button(__('View Orders'), function() {
frappe.set_route('List', 'Sales Order', {blanket_order: frm.doc.name}); frappe.set_route('List', 'Sales Order', {blanket_order: frm.doc.name});
@ -51,11 +52,19 @@ frappe.ui.form.on('Blanket Order', {
set_tc_name_filter: function(frm) { set_tc_name_filter: function(frm) {
if (frm.doc.blanket_order_type === 'Selling') { if (frm.doc.blanket_order_type === 'Selling') {
frm.set_df_property("customer","reqd", 1);
frm.set_df_property("supplier","reqd", 0);
frm.set_value("supplier", "");
frm.set_query("tc_name", function() { frm.set_query("tc_name", function() {
return { filters: { selling: 1 } }; return { filters: { selling: 1 } };
}); });
} }
if (frm.doc.blanket_order_type === 'Purchasing') { if (frm.doc.blanket_order_type === 'Purchasing') {
frm.set_df_property("supplier","reqd", 1);
frm.set_df_property("customer","reqd", 0);
frm.set_value("customer", "");
frm.set_query("tc_name", function() { frm.set_query("tc_name", function() {
return { filters: { buying: 1 } }; return { filters: { buying: 1 } };
}); });

View File

@ -88,7 +88,8 @@
"fieldname": "company", "fieldname": "company",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Company", "label": "Company",
"options": "Company" "options": "Company",
"reqd": 1
}, },
{ {
"fieldname": "section_break_12", "fieldname": "section_break_12",
@ -128,7 +129,7 @@
} }
], ],
"is_submittable": 1, "is_submittable": 1,
"modified": "2019-06-19 11:59:09.279607", "modified": "2019-10-16 13:38:32.302316",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Blanket Order", "name": "Blanket Order",

View File

@ -4,13 +4,21 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import flt from frappe import _
from frappe.utils import flt, getdate
from frappe.model.document import Document from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
from erpnext.stock.doctype.item.item import get_item_defaults from erpnext.stock.doctype.item.item import get_item_defaults
class BlanketOrder(Document): class BlanketOrder(Document):
def validate(self):
self.validate_dates()
def validate_dates(self):
if getdate(self.from_date) > getdate(self.to_date):
frappe.throw(_("From date cannot be greater than To date"))
def update_ordered_qty(self): def update_ordered_qty(self):
ref_doctype = "Sales Order" if self.blanket_order_type == "Selling" else "Purchase Order" ref_doctype = "Sales Order" if self.blanket_order_type == "Selling" else "Purchase Order"
item_ordered_qty = frappe._dict(frappe.db.sql(""" item_ordered_qty = frappe._dict(frappe.db.sql("""

View File

@ -35,12 +35,15 @@ class BOM(WebsiteGenerator):
# name can be BOM/ITEM/001, BOM/ITEM/001-1, BOM-ITEM-001, BOM-ITEM-001-1 # name can be BOM/ITEM/001, BOM/ITEM/001-1, BOM-ITEM-001, BOM-ITEM-001-1
# split by item # split by item
names = [name.split(self.item)[-1][1:] for name in names] names = [name.split(self.item, 1) for name in names]
names = [d[-1][1:] for d in filter(lambda x: len(x) > 1 and x[-1], names)]
# split by (-) if cancelled # split by (-) if cancelled
names = [cint(name.split('-')[-1]) for name in names] if names:
names = [cint(name.split('-')[-1]) for name in names]
idx = max(names) + 1 idx = max(names) + 1
else:
idx = 1
else: else:
idx = 1 idx = 1
@ -289,7 +292,8 @@ class BOM(WebsiteGenerator):
return valuation_rate return valuation_rate
def manage_default_bom(self): def manage_default_bom(self):
""" Uncheck others if current one is selected as default, """ Uncheck others if current one is selected as default or
check the current one as default if it the only bom for the selected item,
update default bom in item master update default bom in item master
""" """
if self.is_default and self.is_active: if self.is_default and self.is_active:
@ -298,6 +302,9 @@ class BOM(WebsiteGenerator):
item = frappe.get_doc("Item", self.item) item = frappe.get_doc("Item", self.item)
if item.default_bom != self.name: if item.default_bom != self.name:
frappe.db.set_value('Item', self.item, 'default_bom', self.name) frappe.db.set_value('Item', self.item, 'default_bom', self.name)
elif not frappe.db.exists(dict(doctype='BOM', docstatus=1, item=self.item, is_default=1)) \
and self.is_active:
frappe.db.set(self, "is_default", 1)
else: else:
frappe.db.set(self, "is_default", 0) frappe.db.set(self, "is_default", 0)
item = frappe.get_doc("Item", self.item) item = frappe.get_doc("Item", self.item)

View File

@ -103,7 +103,7 @@ frappe.ui.form.on('Production Plan', {
${__('Reserved Qty for Production: Raw materials quantity to make manufacturing items.')} ${__('Reserved Qty for Production: Raw materials quantity to make manufacturing items.')}
</li> </li>
<li> <li>
${__('Reserved Qty for Subcontract: Raw materials quantity to make subcotracted items.')} ${__('Reserved Qty for Subcontract: Raw materials quantity to make subcontracted items.')}
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -622,7 +622,7 @@ def get_items_for_material_requests(doc, ignore_existing_ordered_qty=None):
for data in po_items: for data in po_items:
planned_qty = data.get('required_qty') or data.get('planned_qty') planned_qty = data.get('required_qty') or data.get('planned_qty')
ignore_existing_ordered_qty = data.get('ignore_existing_ordered_qty') or ignore_existing_ordered_qty ignore_existing_ordered_qty = data.get('ignore_existing_ordered_qty') or ignore_existing_ordered_qty
warehouse = data.get("warehouse") or warehouse warehouse = warehouse or data.get("warehouse")
item_details = {} item_details = {}
if data.get("bom") or data.get("bom_no"): if data.get("bom") or data.get("bom_no"):

View File

@ -11,9 +11,11 @@ from erpnext.manufacturing.doctype.production_plan.production_plan import get_sa
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.manufacturing.doctype.production_plan.production_plan import get_items_for_material_requests from erpnext.manufacturing.doctype.production_plan.production_plan import get_items_for_material_requests
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
class TestProductionPlan(unittest.TestCase): class TestProductionPlan(unittest.TestCase):
def setUp(self): def setUp(self):
set_perpetual_inventory(0)
for item in ['Test Production Item 1', 'Subassembly Item 1', for item in ['Test Production Item 1', 'Subassembly Item 1',
'Raw Material Item 1', 'Raw Material Item 2']: 'Raw Material Item 1', 'Raw Material Item 2']:
create_item(item, valuation_rate=100) create_item(item, valuation_rate=100)

View File

@ -17,11 +17,11 @@ from erpnext.manufacturing.doctype.production_plan.test_production_plan import m
class TestWorkOrder(unittest.TestCase): class TestWorkOrder(unittest.TestCase):
def setUp(self): def setUp(self):
set_perpetual_inventory(0)
self.warehouse = '_Test Warehouse 2 - _TC' self.warehouse = '_Test Warehouse 2 - _TC'
self.item = '_Test Item' self.item = '_Test Item'
def check_planned_qty(self): def check_planned_qty(self):
set_perpetual_inventory(0)
planned0 = frappe.db.get_value("Bin", {"item_code": "_Test FG Item", planned0 = frappe.db.get_value("Bin", {"item_code": "_Test FG Item",
"warehouse": "_Test Warehouse 1 - _TC"}, "planned_qty") or 0 "warehouse": "_Test Warehouse 1 - _TC"}, "planned_qty") or 0

View File

@ -342,7 +342,7 @@ frappe.ui.form.on("Work Order", {
}, },
project: function(frm) { project: function(frm) {
if(!erpnext.in_production_item_onchange) { if(!erpnext.in_production_item_onchange && !frm.doc.bom_no) {
frm.trigger("production_item"); frm.trigger("production_item");
} }
}, },

View File

@ -640,3 +640,4 @@ erpnext.patches.v12_0.create_default_energy_point_rules
erpnext.patches.v12_0.set_produced_qty_field_in_sales_order_for_work_order erpnext.patches.v12_0.set_produced_qty_field_in_sales_order_for_work_order
erpnext.patches.v12_0.generate_leave_ledger_entries erpnext.patches.v12_0.generate_leave_ledger_entries
erpnext.patches.v12_0.set_default_shopify_app_type erpnext.patches.v12_0.set_default_shopify_app_type
erpnext.patches.v12_0.replace_accounting_with_accounts_in_home_settings

View File

@ -4,7 +4,7 @@ from frappe.model.utils.rename_field import rename_field
def execute(): def execute():
frappe.reload_doc('desk', 'doctype', 'auto_repeat') frappe.reload_doc('automation', 'doctype', 'auto_repeat')
doctypes_to_rename = { doctypes_to_rename = {
'accounts': ['Journal Entry', 'Payment Entry', 'Purchase Invoice', 'Sales Invoice'], 'accounts': ['Journal Entry', 'Payment Entry', 'Purchase Invoice', 'Sales Invoice'],
@ -41,4 +41,4 @@ def drop_columns_from_subscription():
if field in frappe.db.get_table_columns("Subscription"): if field in frappe.db.get_table_columns("Subscription"):
fields_to_drop['Subscription'].append(field) fields_to_drop['Subscription'].append(field)
frappe.model.delete_fields(fields_to_drop, delete=1) frappe.model.delete_fields(fields_to_drop, delete=1)

View File

@ -0,0 +1,5 @@
import frappe
def execute():
frappe.db.sql("""UPDATE `tabUser` SET `home_settings` = REPLACE(`home_settings`, 'Accounting', 'Accounts')""")
frappe.cache().delete_key('home_settings')

View File

@ -11,6 +11,9 @@ frappe.ui.form.on("Project", {
// add a new row and set the project // add a new row and set the project
let time_log = frappe.model.get_new_doc('Timesheet Detail'); let time_log = frappe.model.get_new_doc('Timesheet Detail');
time_log.project = frm.doc.name; time_log.project = frm.doc.name;
time_log.parent = new_doc.name;
time_log.parentfield = 'time_logs';
time_log.parenttype = 'Timesheet';
new_doc.time_logs = [time_log]; new_doc.time_logs = [time_log];
frappe.ui.form.make_quick_entry(doctype, null, null, new_doc); frappe.ui.form.make_quick_entry(doctype, null, null, new_doc);

View File

@ -353,17 +353,35 @@ def get_events(start, end, filters=None):
def get_timesheets_list(doctype, txt, filters, limit_start, limit_page_length=20, order_by="modified"): def get_timesheets_list(doctype, txt, filters, limit_start, limit_page_length=20, order_by="modified"):
user = frappe.session.user user = frappe.session.user
# find customer name from contact. # find customer name from contact.
customer = frappe.db.sql('''SELECT dl.link_name FROM `tabContact` AS c inner join \ customer = ''
`tabDynamic Link` AS dl ON c.first_name=dl.link_name WHERE c.email_id=%s''',user) timesheets = []
contact = frappe.db.exists('Contact', {'user': user})
if contact:
# find customer
contact = frappe.get_doc('Contact', contact)
customer = contact.get_link_for('Customer')
if customer: if customer:
# find list of Sales Invoice for made for customer. sales_invoices = [d.name for d in frappe.get_all('Sales Invoice', filters={'customer': customer})] or [None]
sales_invoice = frappe.db.sql('''SELECT name FROM `tabSales Invoice` WHERE customer = %s''',customer) projects = [d.name for d in frappe.get_all('Project', filters={'customer': customer})]
# Return timesheet related data to web portal. # Return timesheet related data to web portal.
return frappe. db.sql('''SELECT ts.name, tsd.activity_type, ts.status, ts.total_billable_hours, \ timesheets = frappe.db.sql('''
tsd.sales_invoice, tsd.project FROM `tabTimesheet` AS ts inner join `tabTimesheet Detail` \ SELECT
AS tsd ON tsd.parent = ts.name where tsd.sales_invoice IN %s order by\ ts.name, tsd.activity_type, ts.status, ts.total_billable_hours,
end_date asc limit {0} , {1}'''.format(limit_start, limit_page_length), [sales_invoice], as_dict = True) COALESCE(ts.sales_invoice, tsd.sales_invoice) AS sales_invoice, tsd.project
FROM `tabTimesheet` ts, `tabTimesheet Detail` tsd
WHERE tsd.parent = ts.name AND
(
ts.sales_invoice IN %(sales_invoices)s OR
tsd.sales_invoice IN %(sales_invoices)s OR
tsd.project IN %(projects)s
)
ORDER BY `end_date` ASC
LIMIT {0}, {1}
'''.format(limit_start, limit_page_length), dict(sales_invoices=sales_invoices, projects=projects), as_dict=True) #nosec
return timesheets
def get_list_context(context=None): def get_list_context(context=None):
return { return {

View File

@ -1653,6 +1653,11 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
'item_code': item.item_code, 'item_code': item.item_code,
'posting_date': me.frm.doc.posting_date || frappe.datetime.nowdate(), 'posting_date': me.frm.doc.posting_date || frappe.datetime.nowdate(),
} }
if (doc.is_return) {
filters["is_return"] = 1;
}
if (item.warehouse) filters["warehouse"] = item.warehouse; if (item.warehouse) filters["warehouse"] = item.warehouse;
return { return {

View File

@ -31,8 +31,8 @@
"contact_phone", "contact_phone",
"contact_mobile", "contact_mobile",
"contact_email", "contact_email",
"company_address_display",
"company_address", "company_address",
"company_address_display",
"col_break46", "col_break46",
"shipping_address_name", "shipping_address_name",
"shipping_address", "shipping_address",
@ -342,12 +342,13 @@
{ {
"fieldname": "company_address_display", "fieldname": "company_address_display",
"fieldtype": "Small Text", "fieldtype": "Small Text",
"label": "Company Address",
"read_only": 1 "read_only": 1
}, },
{ {
"fieldname": "company_address", "fieldname": "company_address",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Company Address", "label": "Company Address Name",
"options": "Address" "options": "Address"
}, },
{ {
@ -682,10 +683,10 @@
"label": "Additional Discount and Coupon Code" "label": "Additional Discount and Coupon Code"
}, },
{ {
"fieldname": "coupon_code", "fieldname": "coupon_code",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Coupon Code", "label": "Coupon Code",
"options": "Coupon Code" "options": "Coupon Code"
}, },
{ {
"default": "Grand Total", "default": "Grand Total",
@ -1185,6 +1186,7 @@
"default": "0", "default": "0",
"fieldname": "skip_delivery_note", "fieldname": "skip_delivery_note",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 1,
"label": "Skip Delivery Note", "label": "Skip Delivery Note",
"print_hide": 1 "print_hide": 1
} }
@ -1192,7 +1194,7 @@
"icon": "fa fa-file-text", "icon": "fa fa-file-text",
"idx": 105, "idx": 105,
"is_submittable": 1, "is_submittable": 1,
"modified": "2019-10-14 08:46:07.540565", "modified": "2019-10-23 14:26:42.767189",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Sales Order", "name": "Sales Order",
@ -1269,4 +1271,4 @@
"title_field": "title", "title_field": "title",
"track_changes": 1, "track_changes": 1,
"track_seen": 1 "track_seen": 1
} }

View File

@ -205,7 +205,7 @@ class SalesOrder(SellingController):
if self.coupon_code: if self.coupon_code:
from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count
update_coupon_code_count(self.coupon_code,'cancelled') update_coupon_code_count(self.coupon_code,'cancelled')
def update_project(self): def update_project(self):
if frappe.db.get_single_value('Selling Settings', 'sales_update_frequency') != "Each Transaction": if frappe.db.get_single_value('Selling Settings', 'sales_update_frequency') != "Each Transaction":
return return
@ -661,12 +661,15 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False):
if source_parent.project: if source_parent.project:
target.cost_center = frappe.db.get_value("Project", source_parent.project, "cost_center") target.cost_center = frappe.db.get_value("Project", source_parent.project, "cost_center")
if not target.cost_center and target.item_code: if target.item_code:
item = get_item_defaults(target.item_code, source_parent.company) item = get_item_defaults(target.item_code, source_parent.company)
item_group = get_item_group_defaults(target.item_code, source_parent.company) item_group = get_item_group_defaults(target.item_code, source_parent.company)
target.cost_center = item.get("selling_cost_center") \ cost_center = item.get("selling_cost_center") \
or item_group.get("selling_cost_center") or item_group.get("selling_cost_center")
if cost_center:
target.cost_center = cost_center
doclist = get_mapped_doc("Sales Order", source_name, { doclist = get_mapped_doc("Sales Order", source_name, {
"Sales Order": { "Sales Order": {
"doctype": "Sales Invoice", "doctype": "Sales Invoice",

View File

@ -33,6 +33,10 @@ class Company(NestedSet):
return exists return exists
def validate(self): def validate(self):
self.update_default_account = False
if self.is_new():
self.update_default_account = True
self.validate_abbr() self.validate_abbr()
self.validate_default_accounts() self.validate_default_accounts()
self.validate_currency() self.validate_currency()
@ -203,8 +207,8 @@ class Company(NestedSet):
"default_expense_account": "Cost of Goods Sold" "default_expense_account": "Cost of Goods Sold"
}) })
for default_account in default_accounts: if self.update_default_account or frappe.flags.in_test:
if self.is_new() or frappe.flags.in_test: for default_account in default_accounts:
self._set_default_account(default_account, default_accounts.get(default_account)) self._set_default_account(default_account, default_accounts.get(default_account))
if not self.default_income_account: if not self.default_income_account:

View File

@ -22,7 +22,7 @@ class TestCompany(unittest.TestCase):
company.create_chart_of_accounts_based_on = "Existing Company" company.create_chart_of_accounts_based_on = "Existing Company"
company.existing_company = "_Test Company" company.existing_company = "_Test Company"
company.save() company.save()
expected_results = { expected_results = {
"Debtors - CFEC": { "Debtors - CFEC": {
"account_type": "Receivable", "account_type": "Receivable",
@ -37,7 +37,7 @@ class TestCompany(unittest.TestCase):
"parent_account": "Cash In Hand - CFEC" "parent_account": "Cash In Hand - CFEC"
} }
} }
for account, acc_property in expected_results.items(): for account, acc_property in expected_results.items():
acc = frappe.get_doc("Account", account) acc = frappe.get_doc("Account", account)
for prop, val in acc_property.items(): for prop, val in acc_property.items():
@ -50,14 +50,14 @@ class TestCompany(unittest.TestCase):
countries = ["India", "Brazil", "United Arab Emirates", "Canada", "Germany", "France", countries = ["India", "Brazil", "United Arab Emirates", "Canada", "Germany", "France",
"Guatemala", "Indonesia", "Italy", "Mexico", "Nicaragua", "Netherlands", "Singapore", "Guatemala", "Indonesia", "Italy", "Mexico", "Nicaragua", "Netherlands", "Singapore",
"Brazil", "Argentina", "Hungary", "Taiwan"] "Brazil", "Argentina", "Hungary", "Taiwan"]
for country in countries: for country in countries:
templates = get_charts_for_country(country) templates = get_charts_for_country(country)
if len(templates) != 1 and "Standard" in templates: if len(templates) != 1 and "Standard" in templates:
templates.remove("Standard") templates.remove("Standard")
self.assertTrue(templates) self.assertTrue(templates)
for template in templates: for template in templates:
try: try:
company = frappe.new_doc("Company") company = frappe.new_doc("Company")
@ -67,11 +67,11 @@ class TestCompany(unittest.TestCase):
company.create_chart_of_accounts_based_on = "Standard Template" company.create_chart_of_accounts_based_on = "Standard Template"
company.chart_of_accounts = template company.chart_of_accounts = template
company.save() company.save()
account_types = ["Cost of Goods Sold", "Depreciation", account_types = ["Cost of Goods Sold", "Depreciation",
"Expenses Included In Valuation", "Fixed Asset", "Payable", "Receivable", "Expenses Included In Valuation", "Fixed Asset", "Payable", "Receivable",
"Stock Adjustment", "Stock Received But Not Billed", "Bank", "Cash", "Stock"] "Stock Adjustment", "Stock Received But Not Billed", "Bank", "Cash", "Stock"]
for account_type in account_types: for account_type in account_types:
filters = { filters = {
"company": template, "company": template,

View File

@ -62,5 +62,16 @@
"domain": "Manufacturing", "domain": "Manufacturing",
"chart_of_accounts": "Standard", "chart_of_accounts": "Standard",
"default_holiday_list": "_Test Holiday List" "default_holiday_list": "_Test Holiday List"
},
{
"abbr": "TCP1",
"company_name": "_Test Company with perpetual inventory",
"country": "India",
"default_currency": "INR",
"doctype": "Company",
"domain": "Manufacturing",
"chart_of_accounts": "Standard",
"enable_perpetual_inventory": 1,
"default_holiday_list": "_Test Holiday List"
} }
] ]

View File

@ -66,7 +66,7 @@ def place_order():
from erpnext.selling.doctype.quotation.quotation import _make_sales_order from erpnext.selling.doctype.quotation.quotation import _make_sales_order
sales_order = frappe.get_doc(_make_sales_order(quotation.name, ignore_permissions=True)) sales_order = frappe.get_doc(_make_sales_order(quotation.name, ignore_permissions=True))
if not cart_settings.allow_items_not_in_stock: if not cint(cart_settings.allow_items_not_in_stock):
for item in sales_order.get("items"): for item in sales_order.get("items"):
item.reserved_warehouse, is_stock_item = frappe.db.get_value("Item", item.reserved_warehouse, is_stock_item = frappe.db.get_value("Item",
item.item_code, ["website_warehouse", "is_stock_item"]) item.item_code, ["website_warehouse", "is_stock_item"])

View File

@ -21,7 +21,7 @@ def get_warehouse_account_map(company=None):
filters['company'] = company filters['company'] = company
for d in frappe.get_all('Warehouse', for d in frappe.get_all('Warehouse',
fields = ["name", "account", "parent_warehouse", "company"], fields = ["name", "account", "parent_warehouse", "company", "is_group"],
filters = filters, filters = filters,
order_by="lft, rgt"): order_by="lft, rgt"):
if not d.account: if not d.account:

View File

@ -8,10 +8,13 @@ import unittest
from erpnext.stock.doctype.batch.batch import get_batch_qty, UnableToSelectBatchError, get_batch_no from erpnext.stock.doctype.batch.batch import get_batch_qty, UnableToSelectBatchError, get_batch_no
from frappe.utils import cint from frappe.utils import cint
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
class TestBatch(unittest.TestCase): class TestBatch(unittest.TestCase):
def setUp(self):
set_perpetual_inventory(0)
def test_item_has_batch_enabled(self): def test_item_has_batch_enabled(self):
self.assertRaises(ValidationError, frappe.get_doc({ self.assertRaises(ValidationError, frappe.get_doc({
"doctype": "Batch", "doctype": "Batch",

View File

@ -6,9 +6,9 @@ frappe.listview_settings['Delivery Note'] = {
return [__("Return"), "darkgrey", "is_return,=,Yes"]; return [__("Return"), "darkgrey", "is_return,=,Yes"];
} else if (doc.status === "Closed") { } else if (doc.status === "Closed") {
return [__("Closed"), "green", "status,=,Closed"]; return [__("Closed"), "green", "status,=,Closed"];
} else if (doc.grand_total !== 0 && flt(doc.per_billed, 2) < 100) { } else if (flt(doc.per_billed, 2) < 100) {
return [__("To Bill"), "orange", "per_billed,<,100"]; return [__("To Bill"), "orange", "per_billed,<,100"];
} else if (doc.grand_total === 0 || flt(doc.per_billed, 2) == 100) { } else if (flt(doc.per_billed, 2) == 100) {
return [__("Completed"), "green", "per_billed,=,100"]; return [__("Completed"), "green", "per_billed,=,100"];
} }
}, },

View File

@ -20,18 +20,11 @@ from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation \
import create_stock_reconciliation, set_valuation_method import create_stock_reconciliation, set_valuation_method
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order, create_dn_against_so from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order, create_dn_against_so
from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account
from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse
class TestDeliveryNote(unittest.TestCase): class TestDeliveryNote(unittest.TestCase):
def tearDown(self): def setUp(self):
target_warehouse = "_Test Warehouse 1 - _TC" set_perpetual_inventory(0)
company = "_Test Company"
if not frappe.db.exists("Account", target_warehouse):
parent_account = frappe.db.get_value('Account',
{'company': company, 'is_group':1, 'account_type': 'Stock'},'name')
account = create_account(account_name="_Test Warehouse 1", \
account_type="Stock", parent_account= parent_account, company=company)
frappe.db.set_value('Warehouse', target_warehouse, 'account', account)
def test_over_billing_against_dn(self): def test_over_billing_against_dn(self):
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1) frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
@ -68,17 +61,16 @@ class TestDeliveryNote(unittest.TestCase):
self.assertFalse(get_gl_entries("Delivery Note", dn.name)) self.assertFalse(get_gl_entries("Delivery Note", dn.name))
def test_delivery_note_gl_entry(self): def test_delivery_note_gl_entry(self):
company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company') company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
set_perpetual_inventory(1, company)
set_valuation_method("_Test Item", "FIFO") set_valuation_method("_Test Item", "FIFO")
make_stock_entry(target="_Test Warehouse - _TC", qty=5, basic_rate=100) make_stock_entry(target="Stores - TCP1", qty=5, basic_rate=100)
stock_in_hand_account = get_inventory_account('_Test Company') stock_in_hand_account = get_inventory_account('_Test Company with perpetual inventory')
prev_bal = get_balance_on(stock_in_hand_account) prev_bal = get_balance_on(stock_in_hand_account)
dn = create_delivery_note() dn = create_delivery_note(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1")
gl_entries = get_gl_entries("Delivery Note", dn.name) gl_entries = get_gl_entries("Delivery Note", dn.name)
self.assertTrue(gl_entries) self.assertTrue(gl_entries)
@ -88,7 +80,7 @@ class TestDeliveryNote(unittest.TestCase):
expected_values = { expected_values = {
stock_in_hand_account: [0.0, stock_value_difference], stock_in_hand_account: [0.0, stock_value_difference],
"Cost of Goods Sold - _TC": [stock_value_difference, 0.0] "Cost of Goods Sold - TCP1": [stock_value_difference, 0.0]
} }
for i, gle in enumerate(gl_entries): for i, gle in enumerate(gl_entries):
self.assertEqual([gle.debit, gle.credit], expected_values.get(gle.account)) self.assertEqual([gle.debit, gle.credit], expected_values.get(gle.account))
@ -98,7 +90,7 @@ class TestDeliveryNote(unittest.TestCase):
self.assertEqual(bal, prev_bal - stock_value_difference) self.assertEqual(bal, prev_bal - stock_value_difference)
# back dated incoming entry # back dated incoming entry
make_stock_entry(posting_date=add_days(nowdate(), -2), target="_Test Warehouse - _TC", make_stock_entry(posting_date=add_days(nowdate(), -2), target="Stores - TCP1",
qty=5, basic_rate=100) qty=5, basic_rate=100)
gl_entries = get_gl_entries("Delivery Note", dn.name) gl_entries = get_gl_entries("Delivery Note", dn.name)
@ -109,27 +101,25 @@ class TestDeliveryNote(unittest.TestCase):
expected_values = { expected_values = {
stock_in_hand_account: [0.0, stock_value_difference], stock_in_hand_account: [0.0, stock_value_difference],
"Cost of Goods Sold - _TC": [stock_value_difference, 0.0] "Cost of Goods Sold - TCP1": [stock_value_difference, 0.0]
} }
for i, gle in enumerate(gl_entries): for i, gle in enumerate(gl_entries):
self.assertEqual([gle.debit, gle.credit], expected_values.get(gle.account)) self.assertEqual([gle.debit, gle.credit], expected_values.get(gle.account))
dn.cancel() dn.cancel()
self.assertFalse(get_gl_entries("Delivery Note", dn.name)) self.assertFalse(get_gl_entries("Delivery Note", dn.name))
set_perpetual_inventory(0, company)
def test_delivery_note_gl_entry_packing_item(self): def test_delivery_note_gl_entry_packing_item(self):
company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company') company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
set_perpetual_inventory(1, company)
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=10, basic_rate=100) make_stock_entry(item_code="_Test Item", target="Stores - TCP1", qty=10, basic_rate=100)
make_stock_entry(item_code="_Test Item Home Desktop 100", make_stock_entry(item_code="_Test Item Home Desktop 100",
target="_Test Warehouse - _TC", qty=10, basic_rate=100) target="Stores - TCP1", qty=10, basic_rate=100)
stock_in_hand_account = get_inventory_account('_Test Company') stock_in_hand_account = get_inventory_account('_Test Company with perpetual inventory')
prev_bal = get_balance_on(stock_in_hand_account) prev_bal = get_balance_on(stock_in_hand_account)
dn = create_delivery_note(item_code="_Test Product Bundle Item") dn = create_delivery_note(item_code="_Test Product Bundle Item", company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1")
stock_value_diff_rm1 = abs(frappe.db.get_value("Stock Ledger Entry", stock_value_diff_rm1 = abs(frappe.db.get_value("Stock Ledger Entry",
{"voucher_type": "Delivery Note", "voucher_no": dn.name, "item_code": "_Test Item"}, {"voucher_type": "Delivery Note", "voucher_no": dn.name, "item_code": "_Test Item"},
@ -146,7 +136,7 @@ class TestDeliveryNote(unittest.TestCase):
expected_values = { expected_values = {
stock_in_hand_account: [0.0, stock_value_diff], stock_in_hand_account: [0.0, stock_value_diff],
"Cost of Goods Sold - _TC": [stock_value_diff, 0.0] "Cost of Goods Sold - TCP1": [stock_value_diff, 0.0]
} }
for i, gle in enumerate(gl_entries): for i, gle in enumerate(gl_entries):
self.assertEqual([gle.debit, gle.credit], expected_values.get(gle.account)) self.assertEqual([gle.debit, gle.credit], expected_values.get(gle.account))
@ -158,8 +148,6 @@ class TestDeliveryNote(unittest.TestCase):
dn.cancel() dn.cancel()
self.assertFalse(get_gl_entries("Delivery Note", dn.name)) self.assertFalse(get_gl_entries("Delivery Note", dn.name))
set_perpetual_inventory(0, company)
def test_serialized(self): def test_serialized(self):
se = make_serialized_item() se = make_serialized_item()
serial_no = get_serial_nos(se.get("items")[0].serial_no)[0] serial_no = get_serial_nos(se.get("items")[0].serial_no)[0]
@ -218,16 +206,16 @@ class TestDeliveryNote(unittest.TestCase):
self.assertEqual(cstr(serial_no.get(field)), value) self.assertEqual(cstr(serial_no.get(field)), value)
def test_sales_return_for_non_bundled_items(self): def test_sales_return_for_non_bundled_items(self):
company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company') company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
set_perpetual_inventory(1, company)
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, basic_rate=100) make_stock_entry(item_code="_Test Item", target="Stores - TCP1", qty=50, basic_rate=100)
actual_qty_0 = get_qty_after_transaction() actual_qty_0 = get_qty_after_transaction(warehouse="Stores - TCP1")
dn = create_delivery_note(qty=5, rate=500) dn = create_delivery_note(qty=5, rate=500, warehouse="Stores - TCP1", company=company,
expense_account="Cost of Goods Sold - TCP1", cost_center="Main - TCP1")
actual_qty_1 = get_qty_after_transaction() actual_qty_1 = get_qty_after_transaction(warehouse="Stores - TCP1")
self.assertEqual(actual_qty_0 - 5, actual_qty_1) self.assertEqual(actual_qty_0 - 5, actual_qty_1)
# outgoing_rate # outgoing_rate
@ -235,9 +223,10 @@ class TestDeliveryNote(unittest.TestCase):
"voucher_no": dn.name}, "stock_value_difference") / 5 "voucher_no": dn.name}, "stock_value_difference") / 5
# return entry # return entry
dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-2, rate=500) dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-2, rate=500,
company=company, warehouse="Stores - TCP1", expense_account="Cost of Goods Sold - TCP1", cost_center="Main - TCP1")
actual_qty_2 = get_qty_after_transaction() actual_qty_2 = get_qty_after_transaction(warehouse="Stores - TCP1")
self.assertEqual(actual_qty_1 + 2, actual_qty_2) self.assertEqual(actual_qty_1 + 2, actual_qty_2)
@ -246,27 +235,29 @@ class TestDeliveryNote(unittest.TestCase):
["incoming_rate", "stock_value_difference"]) ["incoming_rate", "stock_value_difference"])
self.assertEqual(flt(incoming_rate, 3), abs(flt(outgoing_rate, 3))) self.assertEqual(flt(incoming_rate, 3), abs(flt(outgoing_rate, 3)))
stock_in_hand_account = get_inventory_account('_Test Company', dn1.items[0].warehouse) stock_in_hand_account = get_inventory_account(company, dn1.items[0].warehouse)
gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Delivery Note", gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Delivery Note",
"voucher_no": dn1.name, "account": stock_in_hand_account}, "debit") "voucher_no": dn1.name, "account": stock_in_hand_account}, "debit")
self.assertEqual(gle_warehouse_amount, stock_value_difference) self.assertEqual(gle_warehouse_amount, stock_value_difference)
set_perpetual_inventory(0, company)
def test_return_single_item_from_bundled_items(self): def test_return_single_item_from_bundled_items(self):
company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company') company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
set_perpetual_inventory(1, company)
create_stock_reconciliation(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, rate=100) create_stock_reconciliation(item_code="_Test Item",
create_stock_reconciliation(item_code="_Test Item Home Desktop 100", target="_Test Warehouse - _TC", warehouse="Stores - TCP1", qty=50, rate=100,
qty=50, rate=100) company=company, expense_account = "Stock Adjustment - TCP1")
create_stock_reconciliation(item_code="_Test Item Home Desktop 100",
warehouse="Stores - TCP1", qty=50, rate=100,
company=company, expense_account = "Stock Adjustment - TCP1")
dn = create_delivery_note(item_code="_Test Product Bundle Item", qty=5, rate=500) dn = create_delivery_note(item_code="_Test Product Bundle Item", qty=5, rate=500,
company=company, warehouse="Stores - TCP1",
expense_account="Cost of Goods Sold - TCP1", cost_center="Main - TCP1")
# Qty after delivery # Qty after delivery
actual_qty_1 = get_qty_after_transaction() actual_qty_1 = get_qty_after_transaction(warehouse="Stores - TCP1")
self.assertEqual(actual_qty_1, 25) self.assertEqual(actual_qty_1, 25)
# outgoing_rate # outgoing_rate
@ -274,10 +265,12 @@ class TestDeliveryNote(unittest.TestCase):
"voucher_no": dn.name, "item_code": "_Test Item"}, "stock_value_difference") / 25 "voucher_no": dn.name, "item_code": "_Test Item"}, "stock_value_difference") / 25
# return 'test item' from packed items # return 'test item' from packed items
dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-10, rate=500) dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-10, rate=500,
company=company, warehouse="Stores - TCP1",
expense_account="Cost of Goods Sold - TCP1", cost_center="Main - TCP1")
# qty after return # qty after return
actual_qty_2 = get_qty_after_transaction() actual_qty_2 = get_qty_after_transaction(warehouse="Stores - TCP1")
self.assertEqual(actual_qty_2, 35) self.assertEqual(actual_qty_2, 35)
# Check incoming rate for return entry # Check incoming rate for return entry
@ -286,7 +279,7 @@ class TestDeliveryNote(unittest.TestCase):
["incoming_rate", "stock_value_difference"]) ["incoming_rate", "stock_value_difference"])
self.assertEqual(flt(incoming_rate, 3), abs(flt(outgoing_rate, 3))) self.assertEqual(flt(incoming_rate, 3), abs(flt(outgoing_rate, 3)))
stock_in_hand_account = get_inventory_account('_Test Company', dn1.items[0].warehouse) stock_in_hand_account = get_inventory_account(company, dn1.items[0].warehouse)
# Check gl entry for warehouse # Check gl entry for warehouse
gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Delivery Note", gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Delivery Note",
@ -294,33 +287,33 @@ class TestDeliveryNote(unittest.TestCase):
self.assertEqual(gle_warehouse_amount, stock_value_difference) self.assertEqual(gle_warehouse_amount, stock_value_difference)
set_perpetual_inventory(0, company)
def test_return_entire_bundled_items(self): def test_return_entire_bundled_items(self):
company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company') company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
set_perpetual_inventory(1, company)
create_stock_reconciliation(item_code="_Test Item", create_stock_reconciliation(item_code="_Test Item",
target="_Test Warehouse - _TC", qty=50, rate=100) warehouse="Stores - TCP1", qty=50, rate=100,
company=company, expense_account = "Stock Adjustment - TCP1")
create_stock_reconciliation(item_code="_Test Item Home Desktop 100", create_stock_reconciliation(item_code="_Test Item Home Desktop 100",
target="_Test Warehouse - _TC", qty=50, rate=100) warehouse="Stores - TCP1", qty=50, rate=100,
company=company, expense_account = "Stock Adjustment - TCP1")
actual_qty = get_qty_after_transaction() actual_qty = get_qty_after_transaction(warehouse="Stores - TCP1")
self.assertEqual(actual_qty, 50) self.assertEqual(actual_qty, 50)
dn = create_delivery_note(item_code="_Test Product Bundle Item", dn = create_delivery_note(item_code="_Test Product Bundle Item",
qty=5, rate=500) qty=5, rate=500, company=company, warehouse="Stores - TCP1", expense_account="Cost of Goods Sold - TCP1", cost_center="Main - TCP1")
# qty after return # qty after return
actual_qty = get_qty_after_transaction() actual_qty = get_qty_after_transaction(warehouse="Stores - TCP1")
self.assertEqual(actual_qty, 25) self.assertEqual(actual_qty, 25)
# return bundled item # return bundled item
dn1 = create_delivery_note(item_code='_Test Product Bundle Item', is_return=1, dn1 = create_delivery_note(item_code='_Test Product Bundle Item', is_return=1,
return_against=dn.name, qty=-2, rate=500) return_against=dn.name, qty=-2, rate=500, company=company, warehouse="Stores - TCP1", expense_account="Cost of Goods Sold - TCP1", cost_center="Main - TCP1")
# qty after return # qty after return
actual_qty = get_qty_after_transaction() actual_qty = get_qty_after_transaction(warehouse="Stores - TCP1")
self.assertEqual(actual_qty, 35) self.assertEqual(actual_qty, 35)
# Check incoming rate for return entry # Check incoming rate for return entry
@ -337,8 +330,6 @@ class TestDeliveryNote(unittest.TestCase):
self.assertEqual(gle_warehouse_amount, 1400) self.assertEqual(gle_warehouse_amount, 1400)
set_perpetual_inventory(0, company)
def test_return_for_serialized_items(self): def test_return_for_serialized_items(self):
se = make_serialized_item() se = make_serialized_item()
serial_no = get_serial_nos(se.get("items")[0].serial_no)[0] serial_no = get_serial_nos(se.get("items")[0].serial_no)[0]
@ -375,56 +366,44 @@ class TestDeliveryNote(unittest.TestCase):
}) })
def test_delivery_of_bundled_items_to_target_warehouse(self): def test_delivery_of_bundled_items_to_target_warehouse(self):
company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company') company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
set_perpetual_inventory(1, company)
set_valuation_method("_Test Item", "FIFO") set_valuation_method("_Test Item", "FIFO")
set_valuation_method("_Test Item Home Desktop 100", "FIFO") set_valuation_method("_Test Item Home Desktop 100", "FIFO")
for warehouse in ("_Test Warehouse - _TC", "_Test Warehouse 1 - _TC"): target_warehouse=get_warehouse(company=company, abbr="TCP1",
create_stock_reconciliation(item_code="_Test Item", target=warehouse, warehouse_name="_Test Customer Warehouse").name
qty=100, rate=100)
create_stock_reconciliation(item_code="_Test Item Home Desktop 100", for warehouse in ("Stores - TCP1", target_warehouse):
target=warehouse, qty=100, rate=100) create_stock_reconciliation(item_code="_Test Item", warehouse=warehouse, company = company,
expense_account = "Stock Adjustment - TCP1", qty=500, rate=100)
create_stock_reconciliation(item_code="_Test Item Home Desktop 100", company = company,
expense_account = "Stock Adjustment - TCP1", warehouse=warehouse, qty=500, rate=100)
opening_qty_test_warehouse_1 = get_qty_after_transaction(warehouse="_Test Warehouse 1 - _TC")
dn = create_delivery_note(item_code="_Test Product Bundle Item", dn = create_delivery_note(item_code="_Test Product Bundle Item",
qty=5, rate=500, target_warehouse="_Test Warehouse 1 - _TC", do_not_submit=True) company='_Test Company with perpetual inventory', cost_center = 'Main - TCP1',
expense_account = "Cost of Goods Sold - TCP1", do_not_submit=True, qty=5, rate=500,
warehouse="Stores - TCP1", target_warehouse=target_warehouse)
dn.submit() dn.submit()
# qty after delivery # qty after delivery
actual_qty = get_qty_after_transaction(warehouse="_Test Warehouse - _TC") actual_qty_at_source = get_qty_after_transaction(warehouse="Stores - TCP1")
self.assertEqual(actual_qty, 75) self.assertEqual(actual_qty_at_source, 475)
actual_qty = get_qty_after_transaction(warehouse="_Test Warehouse 1 - _TC") actual_qty_at_target = get_qty_after_transaction(warehouse=target_warehouse)
self.assertEqual(actual_qty, opening_qty_test_warehouse_1 + 25) self.assertEqual(actual_qty_at_target, 525)
# stock value diff for source warehouse # stock value diff for source warehouse for "_Test Item"
# for "_Test Item"
stock_value_difference = frappe.db.get_value("Stock Ledger Entry", stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
{"voucher_type": "Delivery Note", "voucher_no": dn.name, {"voucher_type": "Delivery Note", "voucher_no": dn.name,
"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"}, "item_code": "_Test Item", "warehouse": "Stores - TCP1"},
"stock_value_difference") "stock_value_difference")
# stock value diff for target warehouse # stock value diff for target warehouse
stock_value_difference1 = frappe.db.get_value("Stock Ledger Entry", stock_value_difference1 = frappe.db.get_value("Stock Ledger Entry",
{"voucher_type": "Delivery Note", "voucher_no": dn.name, {"voucher_type": "Delivery Note", "voucher_no": dn.name,
"item_code": "_Test Item", "warehouse": "_Test Warehouse 1 - _TC"}, "item_code": "_Test Item", "warehouse": target_warehouse},
"stock_value_difference")
self.assertEqual(abs(stock_value_difference), stock_value_difference1)
# for "_Test Item Home Desktop 100"
stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
{"voucher_type": "Delivery Note", "voucher_no": dn.name,
"item_code": "_Test Item Home Desktop 100", "warehouse": "_Test Warehouse - _TC"},
"stock_value_difference")
# stock value diff for target warehouse
stock_value_difference1 = frappe.db.get_value("Stock Ledger Entry",
{"voucher_type": "Delivery Note", "voucher_no": dn.name,
"item_code": "_Test Item Home Desktop 100", "warehouse": "_Test Warehouse 1 - _TC"},
"stock_value_difference") "stock_value_difference")
self.assertEqual(abs(stock_value_difference), stock_value_difference1) self.assertEqual(abs(stock_value_difference), stock_value_difference1)
@ -435,21 +414,20 @@ class TestDeliveryNote(unittest.TestCase):
stock_value_difference = abs(frappe.db.sql("""select sum(stock_value_difference) stock_value_difference = abs(frappe.db.sql("""select sum(stock_value_difference)
from `tabStock Ledger Entry` where voucher_type='Delivery Note' and voucher_no=%s from `tabStock Ledger Entry` where voucher_type='Delivery Note' and voucher_no=%s
and warehouse='_Test Warehouse - _TC'""", dn.name)[0][0]) and warehouse='Stores - TCP1'""", dn.name)[0][0])
expected_values = { expected_values = {
"Stock In Hand - _TC": [0.0, stock_value_difference], "Stock In Hand - TCP1": [0.0, stock_value_difference],
"_Test Warehouse 1 - _TC": [stock_value_difference, 0.0] target_warehouse: [stock_value_difference, 0.0]
} }
for i, gle in enumerate(gl_entries): for i, gle in enumerate(gl_entries):
self.assertEqual([gle.debit, gle.credit], expected_values.get(gle.account)) self.assertEqual([gle.debit, gle.credit], expected_values.get(gle.account))
set_perpetual_inventory(0, company)
def test_closed_delivery_note(self): def test_closed_delivery_note(self):
from erpnext.stock.doctype.delivery_note.delivery_note import update_delivery_note_status from erpnext.stock.doctype.delivery_note.delivery_note import update_delivery_note_status
dn = create_delivery_note(do_not_submit=True) dn = create_delivery_note(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1", do_not_submit=True)
dn.submit() dn.submit()
update_delivery_note_status(dn.name, "Closed") update_delivery_note_status(dn.name, "Closed")
@ -574,24 +552,23 @@ class TestDeliveryNote(unittest.TestCase):
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 accounts_settings.allow_cost_center_in_entry_of_bs_account = 1
accounts_settings.save() accounts_settings.save()
cost_center = "_Test Cost Center for BS Account - _TC" cost_center = "_Test Cost Center for BS Account - TCP1"
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company with perpetual inventory")
company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company') company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
set_perpetual_inventory(1, company)
set_valuation_method("_Test Item", "FIFO") set_valuation_method("_Test Item", "FIFO")
make_stock_entry(target="_Test Warehouse - _TC", qty=5, basic_rate=100) make_stock_entry(target="Stores - TCP1", qty=5, basic_rate=100)
stock_in_hand_account = get_inventory_account('_Test Company') stock_in_hand_account = get_inventory_account('_Test Company with perpetual inventory')
dn = create_delivery_note(cost_center=cost_center) dn = create_delivery_note(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', expense_account = "Cost of Goods Sold - TCP1", cost_center=cost_center)
gl_entries = get_gl_entries("Delivery Note", dn.name) gl_entries = get_gl_entries("Delivery Note", dn.name)
self.assertTrue(gl_entries) self.assertTrue(gl_entries)
expected_values = { expected_values = {
"Cost of Goods Sold - _TC": { "Cost of Goods Sold - TCP1": {
"cost_center": cost_center "cost_center": cost_center
}, },
stock_in_hand_account: { stock_in_hand_account: {
@ -600,8 +577,6 @@ class TestDeliveryNote(unittest.TestCase):
} }
for i, gle in enumerate(gl_entries): for i, gle in enumerate(gl_entries):
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
set_perpetual_inventory(0, company)
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
accounts_settings.save() accounts_settings.save()
@ -609,23 +584,22 @@ class TestDeliveryNote(unittest.TestCase):
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
accounts_settings.save() accounts_settings.save()
cost_center = "_Test Cost Center - _TC" cost_center = "Main - TCP1"
company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company') company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
set_perpetual_inventory(1, company)
set_valuation_method("_Test Item", "FIFO") set_valuation_method("_Test Item", "FIFO")
make_stock_entry(target="_Test Warehouse - _TC", qty=5, basic_rate=100) make_stock_entry(target="Stores - TCP1", qty=5, basic_rate=100)
stock_in_hand_account = get_inventory_account('_Test Company') stock_in_hand_account = get_inventory_account('_Test Company with perpetual inventory')
dn = create_delivery_note() dn = create_delivery_note(company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', cost_center = 'Main - TCP1', expense_account = "Cost of Goods Sold - TCP1")
gl_entries = get_gl_entries("Delivery Note", dn.name) gl_entries = get_gl_entries("Delivery Note", dn.name)
self.assertTrue(gl_entries) self.assertTrue(gl_entries)
expected_values = { expected_values = {
"Cost of Goods Sold - _TC": { "Cost of Goods Sold - TCP1": {
"cost_center": cost_center "cost_center": cost_center
}, },
stock_in_hand_account: { stock_in_hand_account: {
@ -635,8 +609,6 @@ class TestDeliveryNote(unittest.TestCase):
for i, gle in enumerate(gl_entries): for i, gle in enumerate(gl_entries):
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
set_perpetual_inventory(0, company)
def test_make_sales_invoice_from_dn_for_returned_qty(self): def test_make_sales_invoice_from_dn_for_returned_qty(self):
from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
@ -702,7 +674,7 @@ def create_delivery_note(**args):
"rate": args.rate or 100, "rate": args.rate or 100,
"conversion_factor": 1.0, "conversion_factor": 1.0,
"allow_zero_valuation_rate": args.allow_zero_valuation_rate or 1, "allow_zero_valuation_rate": args.allow_zero_valuation_rate or 1,
"expense_account": "Cost of Goods Sold - _TC", "expense_account": args.expense_account or "Cost of Goods Sold - _TC",
"cost_center": args.cost_center or "_Test Cost Center - _TC", "cost_center": args.cost_center or "_Test Cost Center - _TC",
"serial_no": args.serial_no, "serial_no": args.serial_no,
"target_warehouse": args.target_warehouse "target_warehouse": args.target_warehouse

View File

@ -12,9 +12,11 @@ from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import
from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt, make_rm_stock_entry from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt, make_rm_stock_entry
import unittest import unittest
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
class TestItemAlternative(unittest.TestCase): class TestItemAlternative(unittest.TestCase):
def setUp(self): def setUp(self):
set_perpetual_inventory(0)
make_items() make_items()
def test_alternative_item_for_subcontract_rm(self): def test_alternative_item_for_subcontract_rm(self):

View File

@ -14,15 +14,15 @@ from erpnext.accounts.doctype.account.test_account import get_inventory_account
class TestLandedCostVoucher(unittest.TestCase): class TestLandedCostVoucher(unittest.TestCase):
def test_landed_cost_voucher(self): def test_landed_cost_voucher(self):
frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1) frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
set_perpetual_inventory(1)
pr = frappe.copy_doc(pr_test_records[0]) pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", get_multiple_items = True, get_taxes_and_charges = True)
pr.submit()
last_sle = frappe.db.get_value("Stock Ledger Entry", { last_sle = frappe.db.get_value("Stock Ledger Entry", {
"voucher_type": pr.doctype, "voucher_type": pr.doctype,
"voucher_no": pr.name, "voucher_no": pr.name,
"item_code": "_Test Item", "item_code": "_Test Item",
"warehouse": "_Test Warehouse - _TC" "warehouse": "Stores - TCP1"
}, },
fieldname=["qty_after_transaction", "stock_value"], as_dict=1) fieldname=["qty_after_transaction", "stock_value"], as_dict=1)
@ -35,7 +35,7 @@ class TestLandedCostVoucher(unittest.TestCase):
"voucher_type": pr.doctype, "voucher_type": pr.doctype,
"voucher_no": pr.name, "voucher_no": pr.name,
"item_code": "_Test Item", "item_code": "_Test Item",
"warehouse": "_Test Warehouse - _TC" "warehouse": "Stores - TCP1"
}, },
fieldname=["qty_after_transaction", "stock_value"], as_dict=1) fieldname=["qty_after_transaction", "stock_value"], as_dict=1)
@ -48,55 +48,56 @@ class TestLandedCostVoucher(unittest.TestCase):
self.assertTrue(gl_entries) self.assertTrue(gl_entries)
stock_in_hand_account = get_inventory_account(pr.company, pr.get("items")[0].warehouse) stock_in_hand_account = get_inventory_account(pr.company, pr.get("items")[0].warehouse)
fixed_asset_account = get_inventory_account(pr.company, pr.get("items")[1].warehouse) fixed_asset_account = get_inventory_account(pr.company, pr.get("items")[1].warehouse)
if stock_in_hand_account == fixed_asset_account: if stock_in_hand_account == fixed_asset_account:
expected_values = { expected_values = {
stock_in_hand_account: [800.0, 0.0], stock_in_hand_account: [800.0, 0.0],
"Stock Received But Not Billed - _TC": [0.0, 500.0], "Stock Received But Not Billed - TCP1": [0.0, 500.0],
"Expenses Included In Valuation - _TC": [0.0, 300.0] "Expenses Included In Valuation - TCP1": [0.0, 300.0]
} }
else: else:
expected_values = { expected_values = {
stock_in_hand_account: [400.0, 0.0], stock_in_hand_account: [400.0, 0.0],
fixed_asset_account: [400.0, 0.0], fixed_asset_account: [400.0, 0.0],
"Stock Received But Not Billed - _TC": [0.0, 500.0], "Stock Received But Not Billed - TCP1": [0.0, 500.0],
"Expenses Included In Valuation - _TC": [0.0, 300.0] "Expenses Included In Valuation - TCP1": [0.0, 300.0]
} }
for gle in gl_entries: for gle in gl_entries:
self.assertEqual(expected_values[gle.account][0], gle.debit) self.assertEqual(expected_values[gle.account][0], gle.debit)
self.assertEqual(expected_values[gle.account][1], gle.credit) self.assertEqual(expected_values[gle.account][1], gle.credit)
set_perpetual_inventory(0)
def test_landed_cost_voucher_against_purchase_invoice(self): def test_landed_cost_voucher_against_purchase_invoice(self):
set_perpetual_inventory(1)
pi = make_purchase_invoice(update_stock=1, posting_date=frappe.utils.nowdate(), pi = make_purchase_invoice(update_stock=1, posting_date=frappe.utils.nowdate(),
posting_time=frappe.utils.nowtime()) posting_time=frappe.utils.nowtime(), cash_bank_account="Cash - TCP1",
company="_Test Company with perpetual inventory", supplier_warehouse="Work In Progress - TCP1",
warehouse= "Stores - TCP1", cost_center = "Main - TCP1",
expense_account ="_Test Account Cost for Goods Sold - TCP1")
last_sle = frappe.db.get_value("Stock Ledger Entry", { last_sle = frappe.db.get_value("Stock Ledger Entry", {
"voucher_type": pi.doctype, "voucher_type": pi.doctype,
"voucher_no": pi.name, "voucher_no": pi.name,
"item_code": "_Test Item", "item_code": "_Test Item",
"warehouse": "_Test Warehouse - _TC" "warehouse": "Stores - TCP1"
}, },
fieldname=["qty_after_transaction", "stock_value"], as_dict=1) fieldname=["qty_after_transaction", "stock_value"], as_dict=1)
submit_landed_cost_voucher("Purchase Invoice", pi.name) submit_landed_cost_voucher("Purchase Invoice", pi.name)
pi_lc_value = frappe.db.get_value("Purchase Invoice Item", {"parent": pi.name}, pi_lc_value = frappe.db.get_value("Purchase Invoice Item", {"parent": pi.name},
"landed_cost_voucher_amount") "landed_cost_voucher_amount")
self.assertEqual(pi_lc_value, 50.0) self.assertEqual(pi_lc_value, 50.0)
last_sle_after_landed_cost = frappe.db.get_value("Stock Ledger Entry", { last_sle_after_landed_cost = frappe.db.get_value("Stock Ledger Entry", {
"voucher_type": pi.doctype, "voucher_type": pi.doctype,
"voucher_no": pi.name, "voucher_no": pi.name,
"item_code": "_Test Item", "item_code": "_Test Item",
"warehouse": "_Test Warehouse - _TC" "warehouse": "Stores - TCP1"
}, },
fieldname=["qty_after_transaction", "stock_value"], as_dict=1) fieldname=["qty_after_transaction", "stock_value"], as_dict=1)
@ -111,21 +112,21 @@ class TestLandedCostVoucher(unittest.TestCase):
expected_values = { expected_values = {
stock_in_hand_account: [300.0, 0.0], stock_in_hand_account: [300.0, 0.0],
"Creditors - _TC": [0.0, 250.0], "Creditors - TCP1": [0.0, 250.0],
"Expenses Included In Valuation - _TC": [0.0, 50.0] "Expenses Included In Valuation - TCP1": [0.0, 50.0]
} }
for gle in gl_entries: for gle in gl_entries:
self.assertEqual(expected_values[gle.account][0], gle.debit) self.assertEqual(expected_values[gle.account][0], gle.debit)
self.assertEqual(expected_values[gle.account][1], gle.credit) self.assertEqual(expected_values[gle.account][1], gle.credit)
set_perpetual_inventory(0)
def test_landed_cost_voucher_for_serialized_item(self):
set_perpetual_inventory(1)
frappe.db.sql("delete from `tabSerial No` where name in ('SN001', 'SN002', 'SN003', 'SN004', 'SN005')")
pr = frappe.copy_doc(pr_test_records[0]) def test_landed_cost_voucher_for_serialized_item(self):
frappe.db.sql("delete from `tabSerial No` where name in ('SN001', 'SN002', 'SN003', 'SN004', 'SN005')")
pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1",
supplier_warehouse = "Work in Progress - TCP1", get_multiple_items = True,
get_taxes_and_charges = True, do_not_submit = True)
pr.items[0].item_code = "_Test Serialized Item" pr.items[0].item_code = "_Test Serialized Item"
pr.items[0].serial_no = "SN001\nSN002\nSN003\nSN004\nSN005" pr.items[0].serial_no = "SN001\nSN002\nSN003\nSN004\nSN005"
pr.submit() pr.submit()
@ -138,39 +139,36 @@ class TestLandedCostVoucher(unittest.TestCase):
["warehouse", "purchase_rate"], as_dict=1) ["warehouse", "purchase_rate"], as_dict=1)
self.assertEqual(serial_no.purchase_rate - serial_no_rate, 5.0) self.assertEqual(serial_no.purchase_rate - serial_no_rate, 5.0)
self.assertEqual(serial_no.warehouse, "_Test Warehouse - _TC") self.assertEqual(serial_no.warehouse, "Stores - TCP1")
set_perpetual_inventory(0)
def test_landed_cost_voucher_for_odd_numbers (self): def test_landed_cost_voucher_for_odd_numbers (self):
set_perpetual_inventory(1)
pr = make_purchase_receipt(do_not_save=True) pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", do_not_save=True)
pr.items[0].cost_center = "_Test Company - _TC" pr.items[0].cost_center = "Main - TCP1"
for x in range(2): for x in range(2):
pr.append("items", { pr.append("items", {
"item_code": "_Test Item", "item_code": "_Test Item",
"warehouse": "_Test Warehouse - _TC", "warehouse": "Stores - TCP1",
"cost_center": "_Test Company - _TC", "cost_center": "Main - TCP1",
"qty": 5, "qty": 5,
"rate": 50 "rate": 50
}) })
pr.submit() pr.submit()
lcv = submit_landed_cost_voucher("Purchase Receipt", pr.name, 123.22) lcv = submit_landed_cost_voucher("Purchase Receipt", pr.name, 123.22)
self.assertEqual(lcv.items[0].applicable_charges, 41.07) self.assertEqual(lcv.items[0].applicable_charges, 41.07)
self.assertEqual(lcv.items[2].applicable_charges, 41.08) self.assertEqual(lcv.items[2].applicable_charges, 41.08)
set_perpetual_inventory(0)
def submit_landed_cost_voucher(receipt_document_type, receipt_document, charges=50): def submit_landed_cost_voucher(receipt_document_type, receipt_document, charges=50):
ref_doc = frappe.get_doc(receipt_document_type, receipt_document) ref_doc = frappe.get_doc(receipt_document_type, receipt_document)
lcv = frappe.new_doc("Landed Cost Voucher") lcv = frappe.new_doc("Landed Cost Voucher")
lcv.company = "_Test Company" lcv.company = "_Test Company"
lcv.distribute_charges_based_on = 'Amount' lcv.distribute_charges_based_on = 'Amount'
lcv.set("purchase_receipts", [{ lcv.set("purchase_receipts", [{
"receipt_document_type": receipt_document_type, "receipt_document_type": receipt_document_type,
"receipt_document": receipt_document, "receipt_document": receipt_document,
@ -178,7 +176,7 @@ def submit_landed_cost_voucher(receipt_document_type, receipt_document, charges=
"posting_date": ref_doc.posting_date, "posting_date": ref_doc.posting_date,
"grand_total": ref_doc.base_grand_total "grand_total": ref_doc.base_grand_total
}]) }])
lcv.set("taxes", [{ lcv.set("taxes", [{
"description": "Insurance Charges", "description": "Insurance Charges",
"account": "_Test Account Insurance Charges - _TC", "account": "_Test Account Insurance Charges - _TC",
@ -186,13 +184,13 @@ def submit_landed_cost_voucher(receipt_document_type, receipt_document, charges=
}]) }])
lcv.insert() lcv.insert()
distribute_landed_cost_on_items(lcv) distribute_landed_cost_on_items(lcv)
lcv.submit() lcv.submit()
return lcv return lcv
def distribute_landed_cost_on_items(lcv): def distribute_landed_cost_on_items(lcv):
based_on = lcv.distribute_charges_based_on.lower() based_on = lcv.distribute_charges_based_on.lower()
total = sum([flt(d.get(based_on)) for d in lcv.get("items")]) total = sum([flt(d.get(based_on)) for d in lcv.get("items")])

View File

@ -14,6 +14,7 @@ from erpnext.stock.doctype.item.test_item import make_item
from six import iteritems from six import iteritems
class TestPurchaseReceipt(unittest.TestCase): class TestPurchaseReceipt(unittest.TestCase):
def setUp(self): def setUp(self):
set_perpetual_inventory(0)
frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1) frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
def test_make_purchase_invoice(self): def test_make_purchase_invoice(self):
@ -32,7 +33,6 @@ class TestPurchaseReceipt(unittest.TestCase):
def test_purchase_receipt_no_gl_entry(self): def test_purchase_receipt_no_gl_entry(self):
company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company') company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company')
set_perpetual_inventory(0, company)
existing_bin_stock_value = frappe.db.get_value("Bin", {"item_code": "_Test Item", existing_bin_stock_value = frappe.db.get_value("Bin", {"item_code": "_Test Item",
"warehouse": "_Test Warehouse - _TC"}, "stock_value") "warehouse": "_Test Warehouse - _TC"}, "stock_value")
@ -52,33 +52,29 @@ class TestPurchaseReceipt(unittest.TestCase):
self.assertFalse(get_gl_entries("Purchase Receipt", pr.name)) self.assertFalse(get_gl_entries("Purchase Receipt", pr.name))
def test_purchase_receipt_gl_entry(self): def test_purchase_receipt_gl_entry(self):
pr = frappe.copy_doc(test_records[0]) pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", get_multiple_items = True, get_taxes_and_charges = True)
set_perpetual_inventory(1, pr.company)
self.assertEqual(cint(erpnext.is_perpetual_inventory_enabled(pr.company)), 1) self.assertEqual(cint(erpnext.is_perpetual_inventory_enabled(pr.company)), 1)
pr.insert()
pr.submit()
gl_entries = get_gl_entries("Purchase Receipt", pr.name) gl_entries = get_gl_entries("Purchase Receipt", pr.name)
self.assertTrue(gl_entries) self.assertTrue(gl_entries)
stock_in_hand_account = get_inventory_account(pr.company, pr.get("items")[0].warehouse) stock_in_hand_account = get_inventory_account(pr.company, pr.items[0].warehouse)
fixed_asset_account = get_inventory_account(pr.company, pr.get("items")[1].warehouse) fixed_asset_account = get_inventory_account(pr.company, pr.items[1].warehouse)
if stock_in_hand_account == fixed_asset_account: if stock_in_hand_account == fixed_asset_account:
expected_values = { expected_values = {
stock_in_hand_account: [750.0, 0.0], stock_in_hand_account: [750.0, 0.0],
"Stock Received But Not Billed - _TC": [0.0, 500.0], "Stock Received But Not Billed - TCP1": [0.0, 500.0],
"Expenses Included In Valuation - _TC": [0.0, 250.0] "Expenses Included In Valuation - TCP1": [0.0, 250.0]
} }
else: else:
expected_values = { expected_values = {
stock_in_hand_account: [375.0, 0.0], stock_in_hand_account: [375.0, 0.0],
fixed_asset_account: [375.0, 0.0], fixed_asset_account: [375.0, 0.0],
"Stock Received But Not Billed - _TC": [0.0, 500.0], "Stock Received But Not Billed - TCP1": [0.0, 500.0],
"Expenses Included In Valuation - _TC": [0.0, 250.0] "Expenses Included In Valuation - TCP1": [0.0, 250.0]
} }
for gle in gl_entries: for gle in gl_entries:
self.assertEqual(expected_values[gle.account][0], gle.debit) self.assertEqual(expected_values[gle.account][0], gle.debit)
self.assertEqual(expected_values[gle.account][1], gle.credit) self.assertEqual(expected_values[gle.account][1], gle.credit)
@ -86,8 +82,6 @@ class TestPurchaseReceipt(unittest.TestCase):
pr.cancel() pr.cancel()
self.assertFalse(get_gl_entries("Purchase Receipt", pr.name)) self.assertFalse(get_gl_entries("Purchase Receipt", pr.name))
set_perpetual_inventory(0, pr.company)
def test_subcontracting(self): def test_subcontracting(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
@ -132,11 +126,10 @@ class TestPurchaseReceipt(unittest.TestCase):
pr.get("items")[0].rejected_warehouse) pr.get("items")[0].rejected_warehouse)
def test_purchase_return(self): def test_purchase_return(self):
set_perpetual_inventory()
pr = make_purchase_receipt() pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1")
return_pr = make_purchase_receipt(is_return=1, return_against=pr.name, qty=-2) return_pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", is_return=1, return_against=pr.name, qty=-2)
# check sle # check sle
outgoing_rate = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Purchase Receipt", outgoing_rate = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Purchase Receipt",
@ -153,28 +146,28 @@ class TestPurchaseReceipt(unittest.TestCase):
expected_values = { expected_values = {
stock_in_hand_account: [0.0, 100.0], stock_in_hand_account: [0.0, 100.0],
"Stock Received But Not Billed - _TC": [100.0, 0.0], "Stock Received But Not Billed - TCP1": [100.0, 0.0],
} }
for gle in gl_entries: for gle in gl_entries:
self.assertEqual(expected_values[gle.account][0], gle.debit) self.assertEqual(expected_values[gle.account][0], gle.debit)
self.assertEqual(expected_values[gle.account][1], gle.credit) self.assertEqual(expected_values[gle.account][1], gle.credit)
set_perpetual_inventory(0)
def test_purchase_return_for_rejected_qty(self): def test_purchase_return_for_rejected_qty(self):
set_perpetual_inventory() from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse
pr = make_purchase_receipt(received_qty=4, qty=2) rejected_warehouse=get_warehouse(company = "_Test Company with perpetual inventory", abbr = " - TCP1", warehouse_name = "_Test Rejected Warehouse").name
print(rejected_warehouse)
pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", received_qty=4, qty=2, rejected_warehouse=rejected_warehouse)
return_pr = make_purchase_receipt(is_return=1, return_against=pr.name, received_qty = -4, qty=-2) return_pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", is_return=1, return_against=pr.name, received_qty = -4, qty=-2, rejected_warehouse=rejected_warehouse)
actual_qty = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Purchase Receipt", actual_qty = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Purchase Receipt",
"voucher_no": return_pr.name, 'warehouse': return_pr.items[0].rejected_warehouse}, "actual_qty") "voucher_no": return_pr.name, 'warehouse': return_pr.items[0].rejected_warehouse}, "actual_qty")
self.assertEqual(actual_qty, -2) self.assertEqual(actual_qty, -2)
set_perpetual_inventory(0)
def test_purchase_return_for_serialized_items(self): def test_purchase_return_for_serialized_items(self):
def _check_serial_no_values(serial_no, field_values): def _check_serial_no_values(serial_no, field_values):
@ -337,7 +330,6 @@ class TestPurchaseReceipt(unittest.TestCase):
pr.cancel() pr.cancel()
serial_nos = frappe.get_all('Serial No', {'asset': asset}, 'name') or [] serial_nos = frappe.get_all('Serial No', {'asset': asset}, 'name') or []
self.assertEquals(len(serial_nos), 0) self.assertEquals(len(serial_nos), 0)
#frappe.db.sql("delete from `tabLocation")
frappe.db.sql("delete from `tabAsset`") frappe.db.sql("delete from `tabAsset`")
def test_purchase_receipt_for_enable_allow_cost_center_in_entry_of_bs_account(self): def test_purchase_receipt_for_enable_allow_cost_center_in_entry_of_bs_account(self):
@ -345,8 +337,8 @@ class TestPurchaseReceipt(unittest.TestCase):
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings') accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
accounts_settings.allow_cost_center_in_entry_of_bs_account = 1 accounts_settings.allow_cost_center_in_entry_of_bs_account = 1
accounts_settings.save() accounts_settings.save()
cost_center = "_Test Cost Center for BS Account - _TC" cost_center = "_Test Cost Center for BS Account - TCP1"
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company with perpetual inventory")
if not frappe.db.exists('Location', 'Test Location'): if not frappe.db.exists('Location', 'Test Location'):
frappe.get_doc({ frappe.get_doc({
@ -354,8 +346,7 @@ class TestPurchaseReceipt(unittest.TestCase):
'location_name': 'Test Location' 'location_name': 'Test Location'
}).insert() }).insert()
set_perpetual_inventory(1, "_Test Company") pr = make_purchase_receipt(cost_center=cost_center, company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1")
pr = make_purchase_receipt(cost_center=cost_center)
stock_in_hand_account = get_inventory_account(pr.company, pr.get("items")[0].warehouse) stock_in_hand_account = get_inventory_account(pr.company, pr.get("items")[0].warehouse)
gl_entries = get_gl_entries("Purchase Receipt", pr.name) gl_entries = get_gl_entries("Purchase Receipt", pr.name)
@ -363,7 +354,7 @@ class TestPurchaseReceipt(unittest.TestCase):
self.assertTrue(gl_entries) self.assertTrue(gl_entries)
expected_values = { expected_values = {
"Stock Received But Not Billed - _TC": { "Stock Received But Not Billed - TCP1": {
"cost_center": cost_center "cost_center": cost_center
}, },
stock_in_hand_account: { stock_in_hand_account: {
@ -373,7 +364,6 @@ class TestPurchaseReceipt(unittest.TestCase):
for i, gle in enumerate(gl_entries): for i, gle in enumerate(gl_entries):
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
set_perpetual_inventory(0, pr.company)
accounts_settings.allow_cost_center_in_entry_of_bs_account = 0 accounts_settings.allow_cost_center_in_entry_of_bs_account = 0
accounts_settings.save() accounts_settings.save()
@ -387,9 +377,7 @@ class TestPurchaseReceipt(unittest.TestCase):
'doctype': 'Location', 'doctype': 'Location',
'location_name': 'Test Location' 'location_name': 'Test Location'
}).insert() }).insert()
pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1")
set_perpetual_inventory(1, "_Test Company")
pr = make_purchase_receipt()
stock_in_hand_account = get_inventory_account(pr.company, pr.get("items")[0].warehouse) stock_in_hand_account = get_inventory_account(pr.company, pr.get("items")[0].warehouse)
gl_entries = get_gl_entries("Purchase Receipt", pr.name) gl_entries = get_gl_entries("Purchase Receipt", pr.name)
@ -397,7 +385,7 @@ class TestPurchaseReceipt(unittest.TestCase):
self.assertTrue(gl_entries) self.assertTrue(gl_entries)
expected_values = { expected_values = {
"Stock Received But Not Billed - _TC": { "Stock Received But Not Billed - TCP1": {
"cost_center": None "cost_center": None
}, },
stock_in_hand_account: { stock_in_hand_account: {
@ -407,8 +395,6 @@ class TestPurchaseReceipt(unittest.TestCase):
for i, gle in enumerate(gl_entries): for i, gle in enumerate(gl_entries):
self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center) self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
set_perpetual_inventory(0, pr.company)
def test_make_purchase_invoice_from_pr_for_returned_qty(self): def test_make_purchase_invoice_from_pr_for_returned_qty(self):
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order, create_pr_against_po from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order, create_pr_against_po
@ -452,6 +438,78 @@ def get_gl_entries(voucher_type, voucher_no):
from `tabGL Entry` where voucher_type=%s and voucher_no=%s from `tabGL Entry` where voucher_type=%s and voucher_no=%s
order by account desc""", (voucher_type, voucher_no), as_dict=1) order by account desc""", (voucher_type, voucher_no), as_dict=1)
def get_taxes(**args):
args = frappe._dict(args)
return [{'account_head': '_Test Account Shipping Charges - TCP1',
'add_deduct_tax': 'Add',
'category': 'Valuation and Total',
'charge_type': 'Actual',
'cost_center': args.cost_center or 'Main - TCP1',
'description': 'Shipping Charges',
'doctype': 'Purchase Taxes and Charges',
'parentfield': 'taxes',
'rate': 100.0,
'tax_amount': 100.0},
{'account_head': '_Test Account VAT - TCP1',
'add_deduct_tax': 'Add',
'category': 'Total',
'charge_type': 'Actual',
'cost_center': args.cost_center or 'Main - TCP1',
'description': 'VAT',
'doctype': 'Purchase Taxes and Charges',
'parentfield': 'taxes',
'rate': 120.0,
'tax_amount': 120.0},
{'account_head': '_Test Account Customs Duty - TCP1',
'add_deduct_tax': 'Add',
'category': 'Valuation',
'charge_type': 'Actual',
'cost_center': args.cost_center or 'Main - TCP1',
'description': 'Customs Duty',
'doctype': 'Purchase Taxes and Charges',
'parentfield': 'taxes',
'rate': 150.0,
'tax_amount': 150.0}]
def get_items(**args):
args = frappe._dict(args)
return [{
"base_amount": 250.0,
"conversion_factor": 1.0,
"description": "_Test Item",
"doctype": "Purchase Receipt Item",
"item_code": "_Test Item",
"item_name": "_Test Item",
"parentfield": "items",
"qty": 5.0,
"rate": 50.0,
"received_qty": 5.0,
"rejected_qty": 0.0,
"stock_uom": "_Test UOM",
"uom": "_Test UOM",
"warehouse": args.warehouse or "_Test Warehouse - _TC",
"cost_center": args.cost_center or "Main - _TC"
},
{
"base_amount": 250.0,
"conversion_factor": 1.0,
"description": "_Test Item Home Desktop 100",
"doctype": "Purchase Receipt Item",
"item_code": "_Test Item Home Desktop 100",
"item_name": "_Test Item Home Desktop 100",
"parentfield": "items",
"qty": 5.0,
"rate": 50.0,
"received_qty": 5.0,
"rejected_qty": 0.0,
"stock_uom": "_Test UOM",
"uom": "_Test UOM",
"warehouse": args.warehouse or "_Test Warehouse 1 - _TC",
"cost_center": args.cost_center or "Main - _TC"
}]
def make_purchase_receipt(**args): def make_purchase_receipt(**args):
if not frappe.db.exists('Location', 'Test Location'): if not frappe.db.exists('Location', 'Test Location'):
frappe.get_doc({ frappe.get_doc({
@ -468,7 +526,7 @@ def make_purchase_receipt(**args):
pr.company = args.company or "_Test Company" pr.company = args.company or "_Test Company"
pr.supplier = args.supplier or "_Test Supplier" pr.supplier = args.supplier or "_Test Supplier"
pr.is_subcontracted = args.is_subcontracted or "No" pr.is_subcontracted = args.is_subcontracted or "No"
pr.supplier_warehouse = "_Test Warehouse 1 - _TC" pr.supplier_warehouse = args.supplier_warehouse or "_Test Warehouse 1 - _TC"
pr.currency = args.currency or "INR" pr.currency = args.currency or "INR"
pr.is_return = args.is_return pr.is_return = args.is_return
pr.return_against = args.return_against pr.return_against = args.return_against
@ -492,6 +550,16 @@ def make_purchase_receipt(**args):
"asset_location": args.location or "Test Location" "asset_location": args.location or "Test Location"
}) })
if args.get_multiple_items:
pr.items = []
for item in get_items(warehouse= args.warehouse, cost_center = args.cost_center or frappe.get_cached_value('Company', pr.company, 'cost_center')):
pr.append("items", item)
if args.get_taxes_and_charges:
for tax in get_taxes():
pr.append("taxes", tax)
if not args.do_not_save: if not args.do_not_save:
pr.insert() pr.insert()
if not args.do_not_submit: if not args.do_not_submit:

View File

@ -83,39 +83,5 @@
} }
], ],
"supplier": "_Test Supplier" "supplier": "_Test Supplier"
},
{
"buying_price_list": "_Test Price List",
"company": "_Test Company",
"conversion_rate": 1.0,
"currency": "INR",
"doctype": "Purchase Receipt",
"base_grand_total": 5000.0,
"is_subcontracted": "Yes",
"base_net_total": 5000.0,
"posting_date": "2013-02-12",
"items": [
{
"base_amount": 5000.0,
"conversion_factor": 1.0,
"description": "_Test FG Item",
"doctype": "Purchase Receipt Item",
"item_code": "_Test FG Item",
"item_name": "_Test FG Item",
"parentfield": "items",
"qty": 10.0,
"rate": 500.0,
"received_qty": 10.0,
"rejected_qty": 0.0,
"stock_uom": "_Test UOM",
"uom": "_Test UOM",
"warehouse": "_Test Warehouse - _TC",
"cost_center": "Main - _TC"
}
],
"supplier": "_Test Supplier",
"supplier_warehouse": "_Test Warehouse - _TC"
} }
] ]

View File

@ -0,0 +1,14 @@
frappe.listview_settings['Serial No'] = {
add_fields: ["is_cancelled", "item_code", "warehouse", "warranty_expiry_date", "delivery_document_type"],
get_indicator: (doc) => {
if (doc.is_cancelled) {
return [__("Cancelled"), "red", "is_cancelled,=,Yes"];
} else if (doc.delivery_document_type) {
return [__("Delivered"), "green", "delivery_document_type,is,set|is_cancelled,=,No"];
} else if (doc.warranty_expiry_date && frappe.datetime.get_diff(doc.warranty_expiry_date, frappe.datetime.nowdate()) <= 0) {
return [__("Expired"), "red", "warranty_expiry_date,not in,|warranty_expiry_date,<=,Today|delivery_document_type,is,not set|is_cancelled,=,No"];
} else {
return [__("Active"), "green", "delivery_document_type,is,not set|is_cancelled,=,No"];
}
}
};

View File

@ -12,6 +12,7 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_pu
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
test_dependencies = ["Item"] test_dependencies = ["Item"]
test_records = frappe.get_test_records('Serial No') test_records = frappe.get_test_records('Serial No')
@ -37,6 +38,8 @@ class TestSerialNo(unittest.TestCase):
self.assertTrue(SerialNoCannotCannotChangeError, sr.save) self.assertTrue(SerialNoCannotCannotChangeError, sr.save)
def test_inter_company_transfer(self): def test_inter_company_transfer(self):
set_perpetual_inventory(0, "_Test Company 1")
set_perpetual_inventory(0)
se = make_serialized_item(target_warehouse="_Test Warehouse - _TC") se = make_serialized_item(target_warehouse="_Test Warehouse - _TC")
serial_nos = get_serial_nos(se.get("items")[0].serial_no) serial_nos = get_serial_nos(se.get("items")[0].serial_no)

View File

@ -241,7 +241,9 @@ class StockEntry(StockController):
for d in self.get("items"): for d in self.get("items"):
if not d.expense_account: if not d.expense_account:
frappe.throw(_("Please enter Difference Account")) frappe.throw(_("Please enter <b>Difference Account</b> or set default <b>Stock Adjustment Account</b> for company {0}")
.format(frappe.bold(self.company)))
elif self.is_opening == "Yes" and frappe.db.get_value("Account", d.expense_account, "report_type") == "Profit and Loss": elif self.is_opening == "Yes" and frappe.db.get_value("Account", d.expense_account, "report_type") == "Profit and Loss":
frappe.throw(_("Difference Account must be a Asset/Liability type account, since this Stock Entry is an Opening Entry"), OpeningEntryAccountError) frappe.throw(_("Difference Account must be a Asset/Liability type account, since this Stock Entry is an Opening Entry"), OpeningEntryAccountError)
@ -329,8 +331,8 @@ class StockEntry(StockController):
if total_completed_qty > flt(completed_qty): if total_completed_qty > flt(completed_qty):
job_card = frappe.db.get_value('Job Card', {'operation_id': d.name}, 'name') job_card = frappe.db.get_value('Job Card', {'operation_id': d.name}, 'name')
if not job_card: if not job_card:
frappe.throw(_("Work Order {0}: job card not found for the operation {1}") frappe.throw(_("Work Order {0}: Job Card not found for the operation {1}")
.format(self.work_order, job_card)) .format(self.work_order, d.operation))
work_order_link = frappe.utils.get_link_to_form('Work Order', self.work_order) work_order_link = frappe.utils.get_link_to_form('Work Order', self.work_order)
job_card_link = frappe.utils.get_link_to_form('Job Card', job_card) job_card_link = frappe.utils.get_link_to_form('Job Card', job_card)

View File

@ -16,7 +16,6 @@ from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.accounts.doctype.account.test_account import get_inventory_account from erpnext.accounts.doctype.account.test_account import get_inventory_account
from erpnext.stock.doctype.stock_entry.stock_entry import move_sample_to_retention_warehouse, make_stock_in_entry from erpnext.stock.doctype.stock_entry.stock_entry import move_sample_to_retention_warehouse, make_stock_in_entry
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import OpeningEntryAccountError from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import OpeningEntryAccountError
from six import iteritems from six import iteritems
def get_sle(**args): def get_sle(**args):
@ -132,20 +131,19 @@ class TestStockEntry(unittest.TestCase):
self.assertTrue(item_code in items) self.assertTrue(item_code in items)
def test_material_receipt_gl_entry(self): def test_material_receipt_gl_entry(self):
company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company') company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
set_perpetual_inventory(1, company)
mr = make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", mr = make_stock_entry(item_code="_Test Item", target="Stores - TCP1", company= company,
qty=50, basic_rate=100, expense_account="Stock Adjustment - _TC") qty=50, basic_rate=100, expense_account="Stock Adjustment - TCP1")
stock_in_hand_account = get_inventory_account(mr.company, mr.get("items")[0].t_warehouse) stock_in_hand_account = get_inventory_account(mr.company, mr.get("items")[0].t_warehouse)
self.check_stock_ledger_entries("Stock Entry", mr.name, self.check_stock_ledger_entries("Stock Entry", mr.name,
[["_Test Item", "_Test Warehouse - _TC", 50.0]]) [["_Test Item", "Stores - TCP1", 50.0]])
self.check_gl_entries("Stock Entry", mr.name, self.check_gl_entries("Stock Entry", mr.name,
sorted([ sorted([
[stock_in_hand_account, 5000.0, 0.0], [stock_in_hand_account, 5000.0, 0.0],
["Stock Adjustment - _TC", 0.0, 5000.0] ["Stock Adjustment - TCP1", 0.0, 5000.0]
]) ])
) )
@ -158,29 +156,26 @@ class TestStockEntry(unittest.TestCase):
where voucher_type='Stock Entry' and voucher_no=%s""", mr.name)) where voucher_type='Stock Entry' and voucher_no=%s""", mr.name))
def test_material_issue_gl_entry(self): def test_material_issue_gl_entry(self):
company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company') company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
set_perpetual_inventory(1, company) make_stock_entry(item_code="_Test Item", target="Stores - TCP1", company= company,
qty=50, basic_rate=100, expense_account="Stock Adjustment - TCP1")
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", mi = make_stock_entry(item_code="_Test Item", source="Stores - TCP1", company=company,
qty=50, basic_rate=100, expense_account="Stock Adjustment - _TC") qty=40, expense_account="Stock Adjustment - TCP1")
mi = make_stock_entry(item_code="_Test Item", source="_Test Warehouse - _TC",
qty=40, expense_account="Stock Adjustment - _TC")
self.check_stock_ledger_entries("Stock Entry", mi.name, self.check_stock_ledger_entries("Stock Entry", mi.name,
[["_Test Item", "_Test Warehouse - _TC", -40.0]]) [["_Test Item", "Stores - TCP1", -40.0]])
stock_in_hand_account = get_inventory_account(mi.company, "_Test Warehouse - _TC") stock_in_hand_account = get_inventory_account(mi.company, "Stores - TCP1")
stock_value_diff = abs(frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Stock Entry", stock_value_diff = abs(frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Stock Entry",
"voucher_no": mi.name}, "stock_value_difference")) "voucher_no": mi.name}, "stock_value_difference"))
self.check_gl_entries("Stock Entry", mi.name, self.check_gl_entries("Stock Entry", mi.name,
sorted([ sorted([
[stock_in_hand_account, 0.0, stock_value_diff], [stock_in_hand_account, 0.0, stock_value_diff],
["Stock Adjustment - _TC", stock_value_diff, 0.0] ["Stock Adjustment - TCP1", stock_value_diff, 0.0]
]) ])
) )
mi.cancel() mi.cancel()
self.assertFalse(frappe.db.sql("""select name from `tabStock Ledger Entry` self.assertFalse(frappe.db.sql("""select name from `tabStock Ledger Entry`
@ -190,16 +185,15 @@ class TestStockEntry(unittest.TestCase):
where voucher_type='Stock Entry' and voucher_no=%s""", mi.name)) where voucher_type='Stock Entry' and voucher_no=%s""", mi.name))
def test_material_transfer_gl_entry(self): def test_material_transfer_gl_entry(self):
company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company') company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
set_perpetual_inventory(1, company)
create_stock_reconciliation(qty=100, rate=100) create_stock_reconciliation(qty=100, rate=100)
mtn = make_stock_entry(item_code="_Test Item", source="_Test Warehouse - _TC", mtn = make_stock_entry(item_code="_Test Item", source="Stores - TCP1",
target="_Test Warehouse 1 - _TC", qty=45) target="Finished Goods - TCP1", qty=45)
self.check_stock_ledger_entries("Stock Entry", mtn.name, self.check_stock_ledger_entries("Stock Entry", mtn.name,
[["_Test Item", "_Test Warehouse - _TC", -45.0], ["_Test Item", "_Test Warehouse 1 - _TC", 45.0]]) [["_Test Item", "Stores - TCP1", -45.0], ["_Test Item", "Finished Goods - TCP1", 45.0]])
stock_in_hand_account = get_inventory_account(mtn.company, mtn.get("items")[0].s_warehouse) stock_in_hand_account = get_inventory_account(mtn.company, mtn.get("items")[0].s_warehouse)
@ -212,7 +206,7 @@ class TestStockEntry(unittest.TestCase):
else: else:
stock_value_diff = abs(frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Stock Entry", stock_value_diff = abs(frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Stock Entry",
"voucher_no": mtn.name, "warehouse": "_Test Warehouse - _TC"}, "stock_value_difference")) "voucher_no": mtn.name, "warehouse": "Stores - TCP1"}, "stock_value_difference"))
self.check_gl_entries("Stock Entry", mtn.name, self.check_gl_entries("Stock Entry", mtn.name,
sorted([ sorted([
@ -255,14 +249,21 @@ class TestStockEntry(unittest.TestCase):
set_perpetual_inventory(0, repack.company) set_perpetual_inventory(0, repack.company)
def test_repack_with_additional_costs(self): def test_repack_with_additional_costs(self):
company = frappe.db.get_value('Warehouse', '_Test Warehouse - _TC', 'company') company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
set_perpetual_inventory(1, company)
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, basic_rate=100) make_stock_entry(item_code="_Test Item", target="Stores - TCP1", company= company,
repack = frappe.copy_doc(test_records[3]) qty=50, basic_rate=100, expense_account="Stock Adjustment - TCP1")
repack = make_stock_entry(company = company, purpose="Repack", do_not_save=True)
repack.posting_date = nowdate() repack.posting_date = nowdate()
repack.posting_time = nowtime() repack.posting_time = nowtime()
items = get_multiple_items()
repack.items = []
for item in items:
repack.append("items", item)
repack.set("additional_costs", [ repack.set("additional_costs", [
{ {
"description": "Actual Oerating Cost", "description": "Actual Oerating Cost",
@ -292,13 +293,12 @@ class TestStockEntry(unittest.TestCase):
self.check_gl_entries("Stock Entry", repack.name, self.check_gl_entries("Stock Entry", repack.name,
sorted([ sorted([
[stock_in_hand_account, 1200, 0.0], [stock_in_hand_account, 1200, 0.0],
["Expenses Included In Valuation - _TC", 0.0, 1200.0] ["Expenses Included In Valuation - TCP1", 0.0, 1200.0]
]) ])
) )
set_perpetual_inventory(0, repack.company)
def check_stock_ledger_entries(self, voucher_type, voucher_no, expected_sle): def check_stock_ledger_entries(self, voucher_type, voucher_no, expected_sle):
expected_sle.sort(key=lambda x: x[0]) expected_sle.sort(key=lambda x: x[1])
# check stock ledger entries # check stock ledger entries
sle = frappe.db.sql("""select item_code, warehouse, actual_qty sle = frappe.db.sql("""select item_code, warehouse, actual_qty
@ -306,7 +306,7 @@ class TestStockEntry(unittest.TestCase):
and voucher_no = %s order by item_code, warehouse, actual_qty""", and voucher_no = %s order by item_code, warehouse, actual_qty""",
(voucher_type, voucher_no), as_list=1) (voucher_type, voucher_no), as_list=1)
self.assertTrue(sle) self.assertTrue(sle)
sle.sort(key=lambda x: x[0]) sle.sort(key=lambda x: x[1])
for i, sle in enumerate(sle): for i, sle in enumerate(sle):
self.assertEqual(expected_sle[i][0], sle[0]) self.assertEqual(expected_sle[i][0], sle[0])
@ -773,14 +773,12 @@ class TestStockEntry(unittest.TestCase):
self.assertEqual(doc.per_transferred, 100) self.assertEqual(doc.per_transferred, 100)
def test_gle_for_opening_stock_entry(self): def test_gle_for_opening_stock_entry(self):
set_perpetual_inventory(1) mr = make_stock_entry(item_code="_Test Item", target="Stores - TCP1", company="_Test Company with perpetual inventory",qty=50, basic_rate=100, expense_account="Stock Adjustment - TCP1", is_opening="Yes", do_not_save=True)
mr = make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC",
qty=50, basic_rate=100, expense_account="Stock Adjustment - _TC", is_opening="Yes", do_not_save=True)
self.assertRaises(OpeningEntryAccountError, mr.save) self.assertRaises(OpeningEntryAccountError, mr.save)
mr.items[0].expense_account = "Temporary Opening - _TC" mr.items[0].expense_account = "Temporary Opening - TCP1"
mr.save() mr.save()
mr.submit() mr.submit()
@ -805,14 +803,42 @@ def make_serialized_item(item_code=None, serial_no=None, target_warehouse=None):
def get_qty_after_transaction(**args): def get_qty_after_transaction(**args):
args = frappe._dict(args) args = frappe._dict(args)
last_sle = get_previous_sle({ last_sle = get_previous_sle({
"item_code": args.item_code or "_Test Item", "item_code": args.item_code or "_Test Item",
"warehouse": args.warehouse or "_Test Warehouse - _TC", "warehouse": args.warehouse or "_Test Warehouse - _TC",
"posting_date": args.posting_date or nowdate(), "posting_date": args.posting_date or nowdate(),
"posting_time": args.posting_time or nowtime() "posting_time": args.posting_time or nowtime()
}) })
return flt(last_sle.get("qty_after_transaction")) return flt(last_sle.get("qty_after_transaction"))
def get_multiple_items():
return [
{
"conversion_factor": 1.0,
"cost_center": "Main - TCP1",
"doctype": "Stock Entry Detail",
"expense_account": "Stock Adjustment - TCP1",
"basic_rate": 100,
"item_code": "_Test Item",
"qty": 50.0,
"s_warehouse": "Stores - TCP1",
"stock_uom": "_Test UOM",
"transfer_qty": 50.0,
"uom": "_Test UOM"
},
{
"conversion_factor": 1.0,
"cost_center": "Main - TCP1",
"doctype": "Stock Entry Detail",
"expense_account": "Stock Adjustment - TCP1",
"basic_rate": 5000,
"item_code": "_Test Item Home Desktop 100",
"qty": 1,
"stock_uom": "_Test UOM",
"t_warehouse": "Stores - TCP1",
"transfer_qty": 1,
"uom": "_Test UOM"
}
]
test_records = frappe.get_test_records('Stock Entry') test_records = frappe.get_test_records('Stock Entry')

View File

@ -7,7 +7,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe, unittest import frappe, unittest
from frappe.utils import flt, nowdate, nowtime from frappe.utils import flt, nowdate, nowtime
from erpnext.accounts.utils import get_stock_and_account_difference from erpnext.accounts.utils import get_stock_and_account_balance
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
from erpnext.stock.stock_ledger import get_previous_sle, update_entries_after from erpnext.stock.stock_ledger import get_previous_sle, update_entries_after
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import EmptyStockReconciliationItemsError, get_items from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import EmptyStockReconciliationItemsError, get_items
@ -21,7 +21,6 @@ class TestStockReconciliation(unittest.TestCase):
def setUpClass(self): def setUpClass(self):
create_batch_or_serial_no_items() create_batch_or_serial_no_items()
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1) frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
insert_existing_sle()
def test_reco_for_fifo(self): def test_reco_for_fifo(self):
self._test_reco_sle_gle("FIFO") self._test_reco_sle_gle("FIFO")
@ -30,7 +29,8 @@ class TestStockReconciliation(unittest.TestCase):
self._test_reco_sle_gle("Moving Average") self._test_reco_sle_gle("Moving Average")
def _test_reco_sle_gle(self, valuation_method): def _test_reco_sle_gle(self, valuation_method):
set_perpetual_inventory() insert_existing_sle(warehouse='Stores - TCP1')
company = frappe.db.get_value('Warehouse', 'Stores - TCP1', 'company')
# [[qty, valuation_rate, posting_date, # [[qty, valuation_rate, posting_date,
# posting_time, expected_stock_value, bin_qty, bin_valuation]] # posting_time, expected_stock_value, bin_qty, bin_valuation]]
input_data = [ input_data = [
@ -46,14 +46,15 @@ class TestStockReconciliation(unittest.TestCase):
last_sle = get_previous_sle({ last_sle = get_previous_sle({
"item_code": "_Test Item", "item_code": "_Test Item",
"warehouse": "_Test Warehouse - _TC", "warehouse": "Stores - TCP1",
"posting_date": d[2], "posting_date": d[2],
"posting_time": d[3] "posting_time": d[3]
}) })
# submit stock reconciliation # submit stock reconciliation
stock_reco = create_stock_reconciliation(qty=d[0], rate=d[1], stock_reco = create_stock_reconciliation(qty=d[0], rate=d[1],
posting_date=d[2], posting_time=d[3]) posting_date=d[2], posting_time=d[3], warehouse="Stores - TCP1",
company=company, expense_account = "Stock Adjustment - TCP1")
# check stock value # check stock value
sle = frappe.db.sql("""select * from `tabStock Ledger Entry` sle = frappe.db.sql("""select * from `tabStock Ledger Entry`
@ -73,17 +74,18 @@ class TestStockReconciliation(unittest.TestCase):
# no gl entries # no gl entries
self.assertTrue(frappe.db.get_value("Stock Ledger Entry", self.assertTrue(frappe.db.get_value("Stock Ledger Entry",
{"voucher_type": "Stock Reconciliation", "voucher_no": stock_reco.name})) {"voucher_type": "Stock Reconciliation", "voucher_no": stock_reco.name}))
self.assertFalse(get_stock_and_account_difference(["_Test Account Stock In Hand - _TC"]))
stock_reco.cancel() acc_bal, stock_bal, wh_list = get_stock_and_account_balance("Stock In Hand - TCP1",
stock_reco.posting_date, stock_reco.company)
self.assertEqual(acc_bal, stock_bal)
self.assertFalse(frappe.db.get_value("Stock Ledger Entry", stock_reco.cancel()
{"voucher_type": "Stock Reconciliation", "voucher_no": stock_reco.name}))
self.assertFalse(frappe.db.get_value("GL Entry", self.assertFalse(frappe.db.get_value("Stock Ledger Entry",
{"voucher_type": "Stock Reconciliation", "voucher_no": stock_reco.name})) {"voucher_type": "Stock Reconciliation", "voucher_no": stock_reco.name}))
set_perpetual_inventory(0) self.assertFalse(frappe.db.get_value("GL Entry",
{"voucher_type": "Stock Reconciliation", "voucher_no": stock_reco.name}))
def test_get_items(self): def test_get_items(self):
create_warehouse("_Test Warehouse Group 1", {"is_group": 1}) create_warehouse("_Test Warehouse Group 1", {"is_group": 1})
@ -203,17 +205,17 @@ class TestStockReconciliation(unittest.TestCase):
stock_doc.cancel() stock_doc.cancel()
def insert_existing_sle(): def insert_existing_sle(warehouse):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
make_stock_entry(posting_date="2012-12-15", posting_time="02:00", item_code="_Test Item", make_stock_entry(posting_date="2012-12-15", posting_time="02:00", item_code="_Test Item",
target="_Test Warehouse - _TC", qty=10, basic_rate=700) target=warehouse, qty=10, basic_rate=700)
make_stock_entry(posting_date="2012-12-25", posting_time="03:00", item_code="_Test Item", make_stock_entry(posting_date="2012-12-25", posting_time="03:00", item_code="_Test Item",
source="_Test Warehouse - _TC", qty=15) source=warehouse, qty=15)
make_stock_entry(posting_date="2013-01-05", posting_time="07:00", item_code="_Test Item", make_stock_entry(posting_date="2013-01-05", posting_time="07:00", item_code="_Test Item",
target="_Test Warehouse - _TC", qty=15, basic_rate=1200) target=warehouse, qty=15, basic_rate=1200)
def create_batch_or_serial_no_items(): def create_batch_or_serial_no_items():
create_warehouse("_Test Warehouse for Stock Reco1", create_warehouse("_Test Warehouse for Stock Reco1",
@ -244,7 +246,10 @@ def create_stock_reconciliation(**args):
sr.company = args.company or "_Test Company" sr.company = args.company or "_Test Company"
sr.expense_account = args.expense_account or \ sr.expense_account = args.expense_account or \
("Stock Adjustment - _TC" if frappe.get_all("Stock Ledger Entry") else "Temporary Opening - _TC") ("Stock Adjustment - _TC" if frappe.get_all("Stock Ledger Entry") else "Temporary Opening - _TC")
sr.cost_center = args.cost_center or "_Test Cost Center - _TC" sr.cost_center = args.cost_center \
or frappe.get_cached_value("Company", sr.company, "cost_center") \
or "_Test Cost Center - _TC"
sr.append("items", { sr.append("items", {
"item_code": args.item_code or "_Test Item", "item_code": args.item_code or "_Test Item",
"warehouse": args.warehouse or "_Test Warehouse - _TC", "warehouse": args.warehouse or "_Test Warehouse - _TC",

View File

@ -1,42 +1,36 @@
[ [
{ {
"company": "_Test Company", "company": "_Test Company",
"create_account_under": "Stock Assets - _TC",
"doctype": "Warehouse", "doctype": "Warehouse",
"warehouse_name": "_Test Warehouse", "warehouse_name": "_Test Warehouse",
"is_group": 0 "is_group": 0
}, },
{ {
"company": "_Test Company", "company": "_Test Company",
"create_account_under": "Stock Assets - _TC",
"doctype": "Warehouse", "doctype": "Warehouse",
"warehouse_name": "_Test Scrap Warehouse", "warehouse_name": "_Test Scrap Warehouse",
"is_group": 0 "is_group": 0
}, },
{ {
"company": "_Test Company", "company": "_Test Company",
"create_account_under": "Fixed Assets - _TC",
"doctype": "Warehouse", "doctype": "Warehouse",
"warehouse_name": "_Test Warehouse 1", "warehouse_name": "_Test Warehouse 1",
"is_group": 0 "is_group": 0
}, },
{ {
"company": "_Test Company", "company": "_Test Company",
"create_account_under": "Fixed Assets - _TC",
"doctype": "Warehouse", "doctype": "Warehouse",
"warehouse_name": "_Test Warehouse 2", "warehouse_name": "_Test Warehouse 2",
"is_group": 0 "is_group": 0
}, },
{ {
"company": "_Test Company", "company": "_Test Company",
"create_account_under": "Stock Assets - _TC",
"doctype": "Warehouse", "doctype": "Warehouse",
"warehouse_name": "_Test Rejected Warehouse", "warehouse_name": "_Test Rejected Warehouse",
"is_group": 0 "is_group": 0
}, },
{ {
"company": "_Test Company 1", "company": "_Test Company 1",
"create_account_under": "Stock Assets - _TC1",
"doctype": "Warehouse", "doctype": "Warehouse",
"warehouse_name": "_Test Warehouse 2", "warehouse_name": "_Test Warehouse 2",
"is_group": 0 "is_group": 0

View File

@ -101,8 +101,7 @@ def create_warehouse(warehouse_name, properties=None, company=None):
w.warehouse_name = warehouse_name w.warehouse_name = warehouse_name
w.parent_warehouse = "_Test Warehouse Group - _TC" w.parent_warehouse = "_Test Warehouse Group - _TC"
w.company = company w.company = company
make_account_for_warehouse(warehouse_name, w) w.account = get_warehouse_account(warehouse_name, company)
w.account = warehouse_id
if properties: if properties:
w.update(properties) w.update(properties)
w.save() w.save()
@ -110,9 +109,40 @@ def create_warehouse(warehouse_name, properties=None, company=None):
else: else:
return warehouse_id return warehouse_id
def make_account_for_warehouse(warehouse_name, warehouse_obj): def get_warehouse(**args):
if not frappe.db.exists("Account", warehouse_name + " - _TC"): args = frappe._dict(args)
parent_account = frappe.db.get_value('Account', if(frappe.db.exists("Warehouse", args.warehouse_name + " - " + args.abbr)):
{'company': warehouse_obj.company, 'is_group':1, 'account_type': 'Stock'},'name') return frappe.get_doc("Warehouse", args.warehouse_name + " - " + args.abbr)
account = create_account(account_name=warehouse_name, \ else:
account_type="Stock", parent_account= parent_account, company=warehouse_obj.company) w = frappe.get_doc({
"company": args.company or "_Test Company",
"doctype": "Warehouse",
"warehouse_name": args.warehouse_name,
"is_group": 0,
"account": get_warehouse_account(args.warehouse_name, args.company, args.abbr)
})
w.insert()
return w
def get_warehouse_account(warehouse_name, company, company_abbr=None):
if not company_abbr:
company_abbr = frappe.get_cached_value("Company", company, 'abbr')
if not frappe.db.exists("Account", warehouse_name + " - " + company_abbr):
return create_account(
account_name=warehouse_name,
parent_account=get_group_stock_account(company, company_abbr),
account_type='Stock',
company=company)
else:
return warehouse_name + " - " + company_abbr
def get_group_stock_account(company, company_abbr=None):
group_stock_account = frappe.db.get_value("Account",
filters={'account_type': 'Stock', 'is_group': 1, 'company': company}, fieldname='name')
if not group_stock_account:
if not company_abbr:
company_abbr = frappe.get_cached_value("Company", company, 'abbr')
group_stock_account = "Current Assets - " + company_abbr
return group_stock_account

View File

@ -89,7 +89,7 @@ def get_item_price_qty_data(filters):
{conditions}""" {conditions}"""
.format(conditions=conditions), filters, as_dict=1) .format(conditions=conditions), filters, as_dict=1)
price_list_names = list(set([frappe.db.escape(item.price_list_name) for item in item_results])) price_list_names = list(set([item.price_list_name for item in item_results]))
buying_price_map = get_price_map(price_list_names, buying=1) buying_price_map = get_price_map(price_list_names, buying=1)
selling_price_map = get_price_map(price_list_names, selling=1) selling_price_map = get_price_map(price_list_names, selling=1)
@ -129,17 +129,15 @@ def get_price_map(price_list_names, buying=0, selling=0):
rate_key = "Buying Rate" if buying else "Selling Rate" rate_key = "Buying Rate" if buying else "Selling Rate"
price_list_key = "Buying Price List" if buying else "Selling Price List" price_list_key = "Buying Price List" if buying else "Selling Price List"
price_list_condition = " and buying=1" if buying else " and selling=1"
pricing_details = frappe.db.sql(""" filters = {"name": ("in", price_list_names)}
select if buying:
name,price_list,price_list_rate filters["buying"] = 1
from else:
`tabItem Price` filters["selling"] = 1
where
name in ({price_list_names}) {price_list_condition} pricing_details = frappe.get_all("Item Price",
""".format(price_list_names=', '.join(['%s']*len(price_list_names)), fields = ["name", "price_list", "price_list_rate"], filters=filters)
price_list_condition=price_list_condition), price_list_names, as_dict=1)
for d in pricing_details: for d in pricing_details:
name = d["name"] name = d["name"]

View File

@ -30,7 +30,7 @@ def get_total_stock(filters):
if filters.get("group_by") == "Warehouse": if filters.get("group_by") == "Warehouse":
if filters.get("company"): if filters.get("company"):
conditions += " AND warehouse.company = '%s'" % frappe.db.escape(filters.get("company"), percent=False) conditions += " AND warehouse.company = %s" % frappe.db.escape(filters.get("company"), percent=False)
conditions += " GROUP BY ledger.warehouse, item.item_code" conditions += " GROUP BY ledger.warehouse, item.item_code"
columns += "'' as company, ledger.warehouse" columns += "'' as company, ledger.warehouse"

View File

@ -22,7 +22,6 @@ class Issue(Document):
return "{0}: {1}".format(_(self.status), self.subject) return "{0}: {1}".format(_(self.status), self.subject)
def validate(self): def validate(self):
self.flags.ignore_disabled = 1
if self.is_new() and self.via_customer_portal: if self.is_new() and self.via_customer_portal:
self.flags.create_communication = True self.flags.create_communication = True

View File

@ -1,13 +1,14 @@
<div class="web-list-item"> <div class="web-list-item transaction-list-item">
<a href="/timesheets?name={{ doc.name | urlencode }}" class="no-decoration"> <div class="row">
<div class="row"> <div class="col-xs-3">
<div class="col-xs-3"> <span class='indicator {{ "red" if doc.status=="Cancelled" else "green" if doc.status=="Billed" else "blue" if doc.status=="Submitted" else "darkgrey" }} small'>
<span class="indicator {{ "red" if doc.status=="Cancelled" else "green" if doc.status=="Billed" else "blue" if doc.status=="Submitted" else "darkgrey" }}">{{ doc.name }}</span> {{ doc.name }}
</div> </span>
<div class="col-xs-3"> Billable Hours: {{ doc.total_billable_hours}} </div>
<div class="col-xs-2"> {{ _(doc.sales_invoice) }} </div>
<div class="col-xs-2"> {{ _(doc.project) }} </div>
<div class="col-xs-2"> {{ _(doc.activity_type) }} </div>
</div> </div>
</a> <div class="col-xs-2 small"> {{ doc.total_billable_hours }} </div>
<div class="col-xs-2 small"> {{ doc.project or '' }} </div>
<div class="col-xs-2 small"> {{ doc.sales_invoice or '' }} </div>
<div class="col-xs-2 small"> {{ _(doc.activity_type) }} </div>
</div>
<!-- <a href="/timesheets?name={{ doc.name | urlencode }}" class="transaction-item-link">Link</a> -->
</div> </div>

View File

@ -1562,7 +1562,7 @@ DocType: Sales Invoice,Payment Due Date,Betaaldatum
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.","Voorbehou Aantal: Hoeveelheid te koop bestel, maar nie afgelewer nie." apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.","Voorbehou Aantal: Hoeveelheid te koop bestel, maar nie afgelewer nie."
DocType: Drug Prescription,Interval UOM,Interval UOM DocType: Drug Prescription,Interval UOM,Interval UOM
DocType: Customer,"Reselect, if the chosen address is edited after save","Herstel, as die gekose adres geredigeer word na die stoor" DocType: Customer,"Reselect, if the chosen address is edited after save","Herstel, as die gekose adres geredigeer word na die stoor"
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcotracted items.,Voorbehou Aantal vir Onderkontrakte: Hoeveelheid grondstowwe om onderverpakte items te maak. apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcontracted items.,Voorbehou Aantal vir Onderkontrakte: Hoeveelheid grondstowwe om onderverpakte items te maak.
apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,Item Variant {0} bestaan reeds met dieselfde eienskappe apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,Item Variant {0} bestaan reeds met dieselfde eienskappe
DocType: Item,Hub Publishing Details,Hub Publishing Details DocType: Item,Hub Publishing Details,Hub Publishing Details
apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening',&#39;Oopmaak&#39; apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening',&#39;Oopmaak&#39;

Can't render this file because it is too large.

View File

@ -1560,7 +1560,7 @@ DocType: Sales Invoice,Payment Due Date,ክፍያ መጠናቀቅ ያለበት
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.",የተያዙ ጫፎች ብዛት ለሽያጭ የታዘዘ ፣ ግን አልደረሰም ፡፡ apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.",የተያዙ ጫፎች ብዛት ለሽያጭ የታዘዘ ፣ ግን አልደረሰም ፡፡
DocType: Drug Prescription,Interval UOM,የጊዜ ክፍተት UOM DocType: Drug Prescription,Interval UOM,የጊዜ ክፍተት UOM
DocType: Customer,"Reselect, if the chosen address is edited after save",የተመረጠው አድራሻ ከተቀመጠ በኋላ ማስተካከያ ከተደረገበት አይምረጡ DocType: Customer,"Reselect, if the chosen address is edited after save",የተመረጠው አድራሻ ከተቀመጠ በኋላ ማስተካከያ ከተደረገበት አይምረጡ
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcotracted items.,የተያዙ ዕቃዎች ለንዑስ-ኮንትራክተር-ንዑስ-ንዑስ ንጥል ነገሮችን ለመስራት ጥሬ ዕቃዎች ብዛት። apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcontracted items.,የተያዙ ዕቃዎች ለንዑስ-ኮንትራክተር-ንዑስ-ንዑስ ንጥል ነገሮችን ለመስራት ጥሬ ዕቃዎች ብዛት።
apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,ንጥል ተለዋጭ {0} ቀድሞውኑ ተመሳሳይ ባሕርያት ጋር አለ apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,ንጥል ተለዋጭ {0} ቀድሞውኑ ተመሳሳይ ባሕርያት ጋር አለ
DocType: Item,Hub Publishing Details,ሃቢ የህትመት ዝርዝሮች DocType: Item,Hub Publishing Details,ሃቢ የህትመት ዝርዝሮች
apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening',&#39;በመክፈት ላይ&#39; apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening',&#39;በመክፈት ላይ&#39;

Can't render this file because it is too large.

View File

@ -1581,7 +1581,7 @@ DocType: Sales Invoice,Payment Due Date,تاريخ استحقاق السداد
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.",الكمية المحجوزة : الكمية المطلوبة لل بيع، ولكن لم يتم تسليمها . apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.",الكمية المحجوزة : الكمية المطلوبة لل بيع، ولكن لم يتم تسليمها .
DocType: Drug Prescription,Interval UOM,الفاصل الزمني أوم DocType: Drug Prescription,Interval UOM,الفاصل الزمني أوم
DocType: Customer,"Reselect, if the chosen address is edited after save",إعادة تحديد، إذا تم تحرير عنوان المختار بعد حفظ DocType: Customer,"Reselect, if the chosen address is edited after save",إعادة تحديد، إذا تم تحرير عنوان المختار بعد حفظ
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcotracted items.,الكمية المحجوزة للعقد من الباطن: كمية المواد الخام لصنع سلع من الباطن. apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcontracted items.,الكمية المحجوزة للعقد من الباطن: كمية المواد الخام لصنع سلع من الباطن.
apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,متغير الصنف {0} موجود بالفعل مع نفس الخصائص apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,متغير الصنف {0} موجود بالفعل مع نفس الخصائص
DocType: Item,Hub Publishing Details,هاب تفاصيل النشر DocType: Item,Hub Publishing Details,هاب تفاصيل النشر
apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening','افتتاحي' apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening','افتتاحي'

Can't render this file because it is too large.

View File

@ -1562,7 +1562,7 @@ DocType: Sales Invoice,Payment Due Date,Дължимото плащане Дат
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.","Количество, запазено: Количество, поръчано за продажба, но не е доставено." apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.","Количество, запазено: Количество, поръчано за продажба, но не е доставено."
DocType: Drug Prescription,Interval UOM,Интервал (мерна единица) DocType: Drug Prescription,Interval UOM,Интервал (мерна единица)
DocType: Customer,"Reselect, if the chosen address is edited after save","Преименувайте отново, ако избраният адрес се редактира след запазване" DocType: Customer,"Reselect, if the chosen address is edited after save","Преименувайте отново, ако избраният адрес се редактира след запазване"
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcotracted items.,"Количество, запазено за подизпълнение: Количество суровини за изработване на извадени продукти." apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcontracted items.,"Количество, запазено за подизпълнение: Количество суровини за изработване на извадени продукти."
apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,Позиция Variant {0} вече съществува с едни и същи атрибути apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,Позиция Variant {0} вече съществува с едни и същи атрибути
DocType: Item,Hub Publishing Details,Подробна информация за издателя DocType: Item,Hub Publishing Details,Подробна информация за издателя
apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening',"""Начален баланс""" apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening',"""Начален баланс"""

Can't render this file because it is too large.

View File

@ -1544,7 +1544,7 @@ DocType: Sales Invoice,Payment Due Date,পরিশোধযোগ্য তা
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.","সংরক্ষিত পরিমাণ: পরিমাণ বিক্রয়ের জন্য অর্ডার করা হয়েছে, তবে বিতরণ করা হয়নি।" apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.","সংরক্ষিত পরিমাণ: পরিমাণ বিক্রয়ের জন্য অর্ডার করা হয়েছে, তবে বিতরণ করা হয়নি।"
DocType: Drug Prescription,Interval UOM,অন্তর্বর্তী UOM DocType: Drug Prescription,Interval UOM,অন্তর্বর্তী UOM
DocType: Customer,"Reselect, if the chosen address is edited after save",সংরক্ষণ করার পরে যদি নির্বাচিত ঠিকানাটি সম্পাদনা করা হয় তবে নির্বাচন বাতিল করুন DocType: Customer,"Reselect, if the chosen address is edited after save",সংরক্ষণ করার পরে যদি নির্বাচিত ঠিকানাটি সম্পাদনা করা হয় তবে নির্বাচন বাতিল করুন
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcotracted items.,সাবকন্ট্রাক্টের জন্য সংরক্ষিত পরিমাণ: উপকোট্রাক্ট আইটেমগুলি তৈরি করতে কাঁচামাল পরিমাণ। apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcontracted items.,সাবকন্ট্রাক্টের জন্য সংরক্ষিত পরিমাণ: উপকোট্রাক্ট আইটেমগুলি তৈরি করতে কাঁচামাল পরিমাণ।
apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,আইটেম ভেরিয়েন্ট {0} ইতিমধ্যে একই বৈশিষ্ট্যাবলী সঙ্গে বিদ্যমান apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,আইটেম ভেরিয়েন্ট {0} ইতিমধ্যে একই বৈশিষ্ট্যাবলী সঙ্গে বিদ্যমান
DocType: Item,Hub Publishing Details,হাব প্রকাশনা বিবরণ DocType: Item,Hub Publishing Details,হাব প্রকাশনা বিবরণ
apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening',' শুরু' apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening',' শুরু'

Can't render this file because it is too large.

View File

@ -1581,7 +1581,7 @@ DocType: Sales Invoice,Payment Due Date,Plaćanje Due Date
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.","Rezervirano Količina : Količina naručiti za prodaju , ali nije dostavljena ." apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.","Rezervirano Količina : Količina naručiti za prodaju , ali nije dostavljena ."
DocType: Drug Prescription,Interval UOM,Interval UOM DocType: Drug Prescription,Interval UOM,Interval UOM
DocType: Customer,"Reselect, if the chosen address is edited after save","Ponovo odaberite, ako je izabrana adresa uređena nakon čuvanja" DocType: Customer,"Reselect, if the chosen address is edited after save","Ponovo odaberite, ako je izabrana adresa uređena nakon čuvanja"
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcotracted items.,Količina rezervisanog za podugovor: Količina sirovina za izradu predmeta koji se oduzimaju. apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcontracted items.,Količina rezervisanog za podugovor: Količina sirovina za izradu predmeta koji se oduzimaju.
apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,Stavka Variant {0} već postoji s istim atributima apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,Stavka Variant {0} već postoji s istim atributima
DocType: Item,Hub Publishing Details,Detalji izdavanja stanice DocType: Item,Hub Publishing Details,Detalji izdavanja stanice
apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening',&#39;Otvaranje&#39; apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening',&#39;Otvaranje&#39;

Can't render this file because it is too large.

View File

@ -1581,7 +1581,7 @@ DocType: Sales Invoice,Payment Due Date,Data de pagament
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.","Reservats Quantitat: Quantitat va ordenar a la venda, però no entregat." apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.","Reservats Quantitat: Quantitat va ordenar a la venda, però no entregat."
DocType: Drug Prescription,Interval UOM,Interval UOM DocType: Drug Prescription,Interval UOM,Interval UOM
DocType: Customer,"Reselect, if the chosen address is edited after save","Torneu a seleccionar, si l&#39;adreça escollida s&#39;edita després de desar-la" DocType: Customer,"Reselect, if the chosen address is edited after save","Torneu a seleccionar, si l&#39;adreça escollida s&#39;edita després de desar-la"
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcotracted items.,Quantitat reservada per al subcontracte: quantitat de matèries primeres per fabricar articles subcontractats. apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcontracted items.,Quantitat reservada per al subcontracte: quantitat de matèries primeres per fabricar articles subcontractats.
apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,Article Variant {0} ja existeix amb els mateixos atributs apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,Article Variant {0} ja existeix amb els mateixos atributs
DocType: Item,Hub Publishing Details,Detalls de publicació del Hub DocType: Item,Hub Publishing Details,Detalls de publicació del Hub
apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening',&#39;Obertura&#39; apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening',&#39;Obertura&#39;

Can't render this file because it is too large.

View File

@ -1580,7 +1580,7 @@ DocType: Sales Invoice,Payment Due Date,Splatno dne
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.","Reserved Množství: Množství objednal k prodeji, ale není doručena." apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.","Reserved Množství: Množství objednal k prodeji, ale není doručena."
DocType: Drug Prescription,Interval UOM,Interval UOM DocType: Drug Prescription,Interval UOM,Interval UOM
DocType: Customer,"Reselect, if the chosen address is edited after save","Znovu vyberte, pokud je zvolená adresa po uložení upravena" DocType: Customer,"Reselect, if the chosen address is edited after save","Znovu vyberte, pokud je zvolená adresa po uložení upravena"
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcotracted items.,Vyhrazeno Množství pro subdodávky: Množství surovin pro výrobu subdodávek. apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcontracted items.,Vyhrazeno Množství pro subdodávky: Množství surovin pro výrobu subdodávek.
apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,Bod Variant {0} již existuje se stejnými vlastnostmi apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,Bod Variant {0} již existuje se stejnými vlastnostmi
DocType: Item,Hub Publishing Details,Podrobnosti o publikování Hubu DocType: Item,Hub Publishing Details,Podrobnosti o publikování Hubu
apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening',"""Otevírací""" apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening',"""Otevírací"""

Can't render this file because it is too large.

View File

@ -1562,7 +1562,7 @@ DocType: Sales Invoice,Payment Due Date,Sidste betalingsdato
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.","Reserveret antal: Mængde bestilt til salg, men ikke leveret." apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.","Reserveret antal: Mængde bestilt til salg, men ikke leveret."
DocType: Drug Prescription,Interval UOM,Interval UOM DocType: Drug Prescription,Interval UOM,Interval UOM
DocType: Customer,"Reselect, if the chosen address is edited after save","Vælg igen, hvis den valgte adresse redigeres efter gem" DocType: Customer,"Reselect, if the chosen address is edited after save","Vælg igen, hvis den valgte adresse redigeres efter gem"
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcotracted items.,Reserveret antal til underentreprise: Råvaremængde til fremstilling af underleverede genstande. apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcontracted items.,Reserveret antal til underentreprise: Råvaremængde til fremstilling af underleverede genstande.
apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,Item Variant {0} findes allerede med samme attributter apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,Item Variant {0} findes allerede med samme attributter
DocType: Item,Hub Publishing Details,Hub Publishing Detaljer DocType: Item,Hub Publishing Details,Hub Publishing Detaljer
apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening','Åbner' apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening','Åbner'

Can't render this file because it is too large.

View File

@ -1581,7 +1581,7 @@ DocType: Sales Invoice,Payment Due Date,Zahlungsstichtag
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.","Reservierte Menge: Für den Verkauf bestellte Menge, aber noch nicht geliefert." apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.","Reservierte Menge: Für den Verkauf bestellte Menge, aber noch nicht geliefert."
DocType: Drug Prescription,Interval UOM,Intervall UOM DocType: Drug Prescription,Interval UOM,Intervall UOM
DocType: Customer,"Reselect, if the chosen address is edited after save","Wählen Sie erneut, wenn die gewählte Adresse nach dem Speichern bearbeitet wird" DocType: Customer,"Reselect, if the chosen address is edited after save","Wählen Sie erneut, wenn die gewählte Adresse nach dem Speichern bearbeitet wird"
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcotracted items.,Reservierte Menge für Lohnbearbeiter: Rohstoffmenge für Lohnbearbeiter. apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcontracted items.,Reservierte Menge für Lohnbearbeiter: Rohstoffmenge für Lohnbearbeiter.
apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,Artikelvariante {0} mit denselben Attributen existiert bereits apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,Artikelvariante {0} mit denselben Attributen existiert bereits
DocType: Item,Hub Publishing Details,Hub-Veröffentlichungsdetails DocType: Item,Hub Publishing Details,Hub-Veröffentlichungsdetails
apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening',"""Eröffnung""" apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening',"""Eröffnung"""

Can't render this file because it is too large.

View File

@ -1581,7 +1581,7 @@ DocType: Sales Invoice,Payment Due Date,Ημερομηνία λήξης προθ
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.",Δεσμευμένη ποσότητα : ποσότητα που παραγγέλθηκε για πώληση αλλά δεν παραδόθηκε. apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.",Δεσμευμένη ποσότητα : ποσότητα που παραγγέλθηκε για πώληση αλλά δεν παραδόθηκε.
DocType: Drug Prescription,Interval UOM,Διαστήματα UOM DocType: Drug Prescription,Interval UOM,Διαστήματα UOM
DocType: Customer,"Reselect, if the chosen address is edited after save","Επαναφέρετε την επιλογή, εάν η επιλεγμένη διεύθυνση επεξεργαστεί μετά την αποθήκευση" DocType: Customer,"Reselect, if the chosen address is edited after save","Επαναφέρετε την επιλογή, εάν η επιλεγμένη διεύθυνση επεξεργαστεί μετά την αποθήκευση"
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcotracted items.,Προβλεπόμενη ποσότητα για υπεργολαβία: Ποσότητα πρώτων υλών για την πραγματοποίηση υποκλάδων. apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcontracted items.,Προβλεπόμενη ποσότητα για υπεργολαβία: Ποσότητα πρώτων υλών για την πραγματοποίηση υποκλάδων.
apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,Θέση Παραλλαγή {0} υπάρχει ήδη με ίδια χαρακτηριστικά apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,Θέση Παραλλαγή {0} υπάρχει ήδη με ίδια χαρακτηριστικά
DocType: Item,Hub Publishing Details,Στοιχεία δημοσίευσης Hub DocType: Item,Hub Publishing Details,Στοιχεία δημοσίευσης Hub
apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening',«Άνοιγμα» apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening',«Άνοιγμα»

Can't render this file because it is too large.

View File

@ -1562,7 +1562,7 @@ DocType: Sales Invoice,Payment Due Date,Fecha de pago
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.","Cantidad Reservada: Cantidad a pedir a la venta , pero no entregado." apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.","Cantidad Reservada: Cantidad a pedir a la venta , pero no entregado."
DocType: Drug Prescription,Interval UOM,Intervalo UOM DocType: Drug Prescription,Interval UOM,Intervalo UOM
DocType: Customer,"Reselect, if the chosen address is edited after save","Vuelva a seleccionar, si la dirección elegida se edita después de guardar" DocType: Customer,"Reselect, if the chosen address is edited after save","Vuelva a seleccionar, si la dirección elegida se edita después de guardar"
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcotracted items.,Cantidad reservada para subcontrato: Cantidad de materias primas para hacer artículos subcotractados. apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcontracted items.,Cantidad reservada para subcontrato: Cantidad de materias primas para hacer artículos subcotractados.
apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,Artículo Variant {0} ya existe con los mismos atributos apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,Artículo Variant {0} ya existe con los mismos atributos
DocType: Item,Hub Publishing Details,Detalle de Publicación del Hub DocType: Item,Hub Publishing Details,Detalle de Publicación del Hub
apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening',&#39;Apertura&#39; apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening',&#39;Apertura&#39;

Can't render this file because it is too large.

View File

@ -1558,7 +1558,7 @@ DocType: Sales Invoice,Payment Due Date,Maksetähtpäevast
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.","Reserveeritud kogus: Müügiks tellitud kogus, kuid tarnimata." apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,"Reserved Qty: Quantity ordered for sale, but not delivered.","Reserveeritud kogus: Müügiks tellitud kogus, kuid tarnimata."
DocType: Drug Prescription,Interval UOM,Intervall UOM DocType: Drug Prescription,Interval UOM,Intervall UOM
DocType: Customer,"Reselect, if the chosen address is edited after save","Kui valite valitud aadressi pärast salvestamist, vali uuesti" DocType: Customer,"Reselect, if the chosen address is edited after save","Kui valite valitud aadressi pärast salvestamist, vali uuesti"
apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcotracted items.,Allhankelepingu jaoks reserveeritud kogus: Tooraine kogus alamhangete jaoks. apps/erpnext/erpnext/manufacturing/doctype/production_plan/production_plan.js,Reserved Qty for Subcontract: Raw materials quantity to make subcontracted items.,Allhankelepingu jaoks reserveeritud kogus: Tooraine kogus alamhangete jaoks.
apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,Punkt Variant {0} on juba olemas sama atribuute apps/erpnext/erpnext/stock/doctype/item/item.js,Item Variant {0} already exists with same attributes,Punkt Variant {0} on juba olemas sama atribuute
DocType: Item,Hub Publishing Details,Hubi avaldamise üksikasjad DocType: Item,Hub Publishing Details,Hubi avaldamise üksikasjad
apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening',&quot;Avamine&quot; apps/erpnext/erpnext/stock/report/stock_ledger/stock_ledger.py,'Opening',&quot;Avamine&quot;

Can't render this file because it is too large.

Some files were not shown because too many files have changed in this diff Show More