Compare commits
116 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
577a3acaac | ||
|
|
5e46ce8a50 | ||
|
|
3b7342b7b5 | ||
|
|
531077e504 | ||
|
|
147918ed66 | ||
|
|
9bbfca9226 | ||
|
|
f1a07ff105 | ||
|
|
fb6e434315 | ||
|
|
3811d96feb | ||
|
|
1017615d02 | ||
|
|
056d627f46 | ||
|
|
663bfeacf4 | ||
|
|
e918ebd721 | ||
|
|
0dc18f0102 | ||
|
|
8ddc882a66 | ||
|
|
49365d0982 | ||
|
|
50c29c7d0f | ||
|
|
bf492122f8 | ||
|
|
38d0ed9f3a | ||
|
|
248a65b37d | ||
|
|
78f86e9385 | ||
|
|
d9f3e0c275 | ||
|
|
c40451ee2e | ||
|
|
6644406185 | ||
|
|
b3a962e121 | ||
|
|
8a28ccfa2f | ||
|
|
0a75fa09ef | ||
|
|
17a16eeaf4 | ||
|
|
3d3f0bcf54 | ||
|
|
bc8b20ae3c | ||
|
|
daf344e5fd | ||
|
|
7916792f99 | ||
|
|
27e37e68b2 | ||
|
|
a538f8a24a | ||
|
|
70ec88b733 | ||
|
|
4f0e5db216 | ||
|
|
79ed124939 | ||
|
|
c0a3cd603b | ||
|
|
7c6f990cf9 | ||
|
|
7820b171d3 | ||
|
|
0cf4cc283c | ||
|
|
9a4b173b88 | ||
|
|
6d83454237 | ||
|
|
ff231b5e62 | ||
|
|
4d74216147 | ||
|
|
b7e5ad0a31 | ||
|
|
e435592d64 | ||
|
|
7ddde8dc3a | ||
|
|
fce2881de6 | ||
|
|
e96e83d557 | ||
|
|
1b2944e871 | ||
|
|
074e73a0dd | ||
|
|
8923801881 | ||
|
|
cfafe93391 | ||
|
|
6c48ef781b | ||
|
|
adeb976a1b | ||
|
|
bb19b91ef9 | ||
|
|
bfa7f171bd | ||
|
|
b96c014daf | ||
|
|
e0c83e22d9 | ||
|
|
099ad0f5e1 | ||
|
|
cf9746dd84 | ||
|
|
b70712dbba | ||
|
|
4c057fe693 | ||
|
|
ee8ff51d60 | ||
|
|
9974b16c32 | ||
|
|
9e5f319d80 | ||
|
|
4f614b4030 | ||
|
|
3d458e973e | ||
|
|
3a19a71262 | ||
|
|
29d1a1c593 | ||
|
|
3b90de558f | ||
|
|
af7e31acb3 | ||
|
|
a2c9d35efb | ||
|
|
4c058f4056 | ||
|
|
ad67b84d43 | ||
|
|
4e81e4065b | ||
|
|
81332789cb | ||
|
|
82d7c0c9eb | ||
|
|
d60235e239 | ||
|
|
9b50b0a762 | ||
|
|
21e14c4c98 | ||
|
|
16edacebc7 | ||
|
|
75027b4d54 | ||
|
|
b0bd99266d | ||
|
|
b9e04815f8 | ||
|
|
18ccc27b1b | ||
|
|
95f1fe92e2 | ||
|
|
b783f519ee | ||
|
|
996a1010cb | ||
|
|
32a9dfd983 | ||
|
|
1394509343 | ||
|
|
0f31c36b2c | ||
|
|
14ac8f71b7 | ||
|
|
6a92d51383 | ||
|
|
7c831c3fe5 | ||
|
|
778ff463af | ||
|
|
70a31d5402 | ||
|
|
48f5fa69f3 | ||
|
|
83a2d12cd2 | ||
|
|
e539297e53 | ||
|
|
bbc3d015a3 | ||
|
|
e4475c635d | ||
|
|
8370cb3e71 | ||
|
|
4073880ecf | ||
|
|
763c7a56dc | ||
|
|
556fbc487d | ||
|
|
f37d4337a4 | ||
|
|
525ab0a925 | ||
|
|
930e7f5578 | ||
|
|
d14e15d432 | ||
|
|
4d3a18890b | ||
|
|
b14401c320 | ||
|
|
b2a3f2d386 | ||
|
|
6b679c45df | ||
|
|
e3d2643f2b |
@@ -14,6 +14,8 @@ install:
|
||||
- sudo apt-get update
|
||||
- sudo apt-get purge -y mysql-common
|
||||
- sudo apt-get install mariadb-server mariadb-common libmariadbclient-dev
|
||||
- ./ci/fix-mariadb.sh
|
||||
|
||||
- wget http://downloads.sourceforge.net/project/wkhtmltopdf/0.12.1/wkhtmltox-0.12.1_linux-precise-amd64.deb
|
||||
- sudo dpkg -i wkhtmltox-0.12.1_linux-precise-amd64.deb
|
||||
- CFLAGS=-O0 pip install git+https://github.com/frappe/frappe.git@develop
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Includes Accounting, Inventory, CRM, Sales, Purchase, Projects, HRMS. Built on Python / MariaDB.
|
||||
|
||||
ERPNext is built on [frappe](https://github.com/frappe/frappe)
|
||||
ERPNext is built on [frappe](https://github.com/frappe/frappe) Python Framework.
|
||||
|
||||
- [User Guide](http://erpnext.org/user-guide.html)
|
||||
- [Getting Help](http://erpnext.org/getting-help.html)
|
||||
@@ -21,7 +21,7 @@ Use the bench, https://github.com/frappe/bench
|
||||
|
||||
1. go to "/login"
|
||||
1. Administrator user name: "Administrator"
|
||||
1. Administrator passowrd "admin"
|
||||
1. Administrator password: "admin"
|
||||
|
||||
### Download and Install
|
||||
|
||||
|
||||
11
ci/fix-mariadb.sh
Executable file
11
ci/fix-mariadb.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
# stolen from http://cgit.drupalcode.org/octopus/commit/?id=db4f837
|
||||
includedir=`mysql_config --variable=pkgincludedir`
|
||||
thiscwd=`pwd`
|
||||
_THIS_DB_VERSION=`mysql -V 2>&1 | tr -d "\n" | cut -d" " -f6 | awk '{ print $1}' | cut -d"-" -f1 | awk '{ print $1}' | sed "s/[\,']//g"`
|
||||
if [ "$_THIS_DB_VERSION" = "5.5.40" ] && [ ! -e "$includedir-$_THIS_DB_VERSION-fixed.log" ] ; then
|
||||
cd $includedir
|
||||
sudo patch -p1 < $thiscwd/ci/my_config.h.patch &> /dev/null
|
||||
sudo touch $includedir-$_THIS_DB_VERSION-fixed.log
|
||||
fi
|
||||
22
ci/my_config.h.patch
Normal file
22
ci/my_config.h.patch
Normal file
@@ -0,0 +1,22 @@
|
||||
diff -burp a/my_config.h b/my_config.h
|
||||
--- a/my_config.h 2014-10-09 19:32:46.000000000 -0400
|
||||
+++ b/my_config.h 2014-10-09 19:35:12.000000000 -0400
|
||||
@@ -641,17 +641,4 @@
|
||||
#define SIZEOF_TIME_T 8
|
||||
/* #undef TIME_T_UNSIGNED */
|
||||
|
||||
-/*
|
||||
- stat structure (from <sys/stat.h>) is conditionally defined
|
||||
- to have different layout and size depending on the defined macros.
|
||||
- The correct macro is defined in my_config.h, which means it MUST be
|
||||
- included first (or at least before <features.h> - so, practically,
|
||||
- before including any system headers).
|
||||
-
|
||||
- __GLIBC__ is defined in <features.h>
|
||||
-*/
|
||||
-#ifdef __GLIBC__
|
||||
-#error <my_config.h> MUST be included first!
|
||||
-#endif
|
||||
-
|
||||
#endif
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = '4.4.0'
|
||||
__version__ = '4.7.0'
|
||||
|
||||
@@ -76,13 +76,14 @@ class JournalVoucher(AccountsController):
|
||||
|
||||
def validate_entries_for_advance(self):
|
||||
for d in self.get('entries'):
|
||||
if not d.is_advance and not d.against_voucher and \
|
||||
not d.against_invoice and not d.against_jv:
|
||||
if not (d.against_voucher and d.against_invoice and d.against_jv):
|
||||
master_type = frappe.db.get_value("Account", d.account, "master_type")
|
||||
if (master_type == 'Customer' and flt(d.credit) > 0) or \
|
||||
(master_type == 'Supplier' and flt(d.debit) > 0):
|
||||
msgprint(_("Row {0}: Please check 'Is Advance' against Account {1} if this \
|
||||
is an advance entry.").format(d.idx, d.account))
|
||||
if not d.is_advance:
|
||||
msgprint(_("Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry.").format(d.idx, d.account))
|
||||
elif (d.against_sales_order or d.against_purchase_order) and d.is_advance != "Yes":
|
||||
frappe.throw(_("Row {0}: Payment against Sales/Purchase Order should always be marked as advance").format(d.idx))
|
||||
|
||||
def validate_against_jv(self):
|
||||
for d in self.get('entries'):
|
||||
@@ -177,7 +178,7 @@ class JournalVoucher(AccountsController):
|
||||
def validate_against_order_fields(self, doctype, payment_against_voucher):
|
||||
for voucher_no, payment_list in payment_against_voucher.items():
|
||||
voucher_properties = frappe.db.get_value(doctype, voucher_no,
|
||||
["docstatus", "per_billed", "advance_paid", "grand_total"])
|
||||
["docstatus", "per_billed", "status", "advance_paid", "grand_total"])
|
||||
|
||||
if voucher_properties[0] != 1:
|
||||
frappe.throw(_("{0} {1} is not submitted").format(doctype, voucher_no))
|
||||
@@ -185,7 +186,10 @@ class JournalVoucher(AccountsController):
|
||||
if flt(voucher_properties[1]) >= 100:
|
||||
frappe.throw(_("{0} {1} is fully billed").format(doctype, voucher_no))
|
||||
|
||||
if flt(voucher_properties[3]) < flt(voucher_properties[2]) + flt(sum(payment_list)):
|
||||
if cstr(voucher_properties[2]) == "Stopped":
|
||||
frappe.throw(_("{0} {1} is stopped").format(doctype, voucher_no))
|
||||
|
||||
if flt(voucher_properties[4]) < flt(voucher_properties[3]) + flt(sum(payment_list)):
|
||||
frappe.throw(_("Advance paid against {0} {1} cannot be greater \
|
||||
than Grand Total {2}").format(doctype, voucher_no, voucher_properties[3]))
|
||||
|
||||
|
||||
@@ -91,6 +91,7 @@ def get_orders_to_be_billed(party_type, party_name):
|
||||
where
|
||||
%s = %s
|
||||
and docstatus = 1
|
||||
and ifnull(status, "") != "Stopped"
|
||||
and ifnull(grand_total, 0) > ifnull(advance_paid, 0)
|
||||
and ifnull(per_billed, 0) < 100.0
|
||||
""" % (voucher_type, 'customer' if party_type == "Customer" else 'supplier', '%s'),
|
||||
|
||||
@@ -31,9 +31,10 @@ class TestPaymentTool(unittest.TestCase):
|
||||
"customer": "_Test Customer 3"
|
||||
})
|
||||
|
||||
jv_against_so1 = self.create_against_jv(jv_test_records[0], {
|
||||
jv_against_so1 = self.create_against_jv(jv_test_records[0], {
|
||||
"account": "_Test Customer 3 - _TC",
|
||||
"against_sales_order": so1.name
|
||||
"against_sales_order": so1.name,
|
||||
"is_advance": "Yes"
|
||||
})
|
||||
|
||||
|
||||
@@ -42,10 +43,11 @@ class TestPaymentTool(unittest.TestCase):
|
||||
"customer": "_Test Customer 3"
|
||||
})
|
||||
|
||||
jv_against_so2 = self.create_against_jv(jv_test_records[0], {
|
||||
jv_against_so2 = self.create_against_jv(jv_test_records[0], {
|
||||
"account": "_Test Customer 3 - _TC",
|
||||
"against_sales_order": so2.name,
|
||||
"credit": 1000
|
||||
"credit": 1000,
|
||||
"is_advance": "Yes"
|
||||
})
|
||||
po = self.create_voucher(po_test_records[1], {
|
||||
"supplier": "_Test Supplier 1"
|
||||
@@ -54,20 +56,20 @@ class TestPaymentTool(unittest.TestCase):
|
||||
#Create SI with partial outstanding
|
||||
si1 = self.create_voucher(si_test_records[0], {
|
||||
"customer": "_Test Customer 3",
|
||||
"debit_to": "_Test Customer 3 - _TC"
|
||||
"debit_to": "_Test Customer 3 - _TC"
|
||||
})
|
||||
|
||||
jv_against_si1 = self.create_against_jv(jv_test_records[0], {
|
||||
|
||||
jv_against_si1 = self.create_against_jv(jv_test_records[0], {
|
||||
"account": "_Test Customer 3 - _TC",
|
||||
"against_invoice": si1.name
|
||||
})
|
||||
#Create SI with no outstanding
|
||||
si2 = self.create_voucher(si_test_records[0], {
|
||||
"customer": "_Test Customer 3",
|
||||
"debit_to": "_Test Customer 3 - _TC"
|
||||
"debit_to": "_Test Customer 3 - _TC"
|
||||
})
|
||||
|
||||
jv_against_si2 = self.create_against_jv(jv_test_records[0], {
|
||||
|
||||
jv_against_si2 = self.create_against_jv(jv_test_records[0], {
|
||||
"account": "_Test Customer 3 - _TC",
|
||||
"against_invoice": si2.name,
|
||||
"credit": 561.80
|
||||
@@ -75,7 +77,7 @@ class TestPaymentTool(unittest.TestCase):
|
||||
|
||||
pi = self.create_voucher(pi_test_records[0], {
|
||||
"supplier": "_Test Supplier 1",
|
||||
"credit_to": "_Test Supplier 1 - _TC"
|
||||
"credit_to": "_Test Supplier 1 - _TC"
|
||||
})
|
||||
|
||||
#Create a dict containing properties and expected values
|
||||
@@ -137,7 +139,7 @@ class TestPaymentTool(unittest.TestCase):
|
||||
payment_tool_doc.set(k, v)
|
||||
|
||||
self.check_outstanding_vouchers(payment_tool_doc, args, expected_outstanding)
|
||||
|
||||
|
||||
|
||||
def check_outstanding_vouchers(self, doc, args, expected_outstanding):
|
||||
from erpnext.accounts.doctype.payment_tool.payment_tool import get_outstanding_vouchers
|
||||
@@ -161,7 +163,7 @@ class TestPaymentTool(unittest.TestCase):
|
||||
|
||||
new_jv = paytool.make_journal_voucher()
|
||||
|
||||
#Create a list of expected values as [party account, payment against, against_jv, against_invoice,
|
||||
#Create a list of expected values as [party account, payment against, against_jv, against_invoice,
|
||||
#against_voucher, against_sales_order, against_purchase_order]
|
||||
expected_values = [
|
||||
[paytool.party_account, 100.00, expected_outstanding.get("Journal Voucher")[0], None, None, None, None],
|
||||
@@ -171,7 +173,7 @@ class TestPaymentTool(unittest.TestCase):
|
||||
[paytool.party_account, 100.00, None, None, None, None, expected_outstanding.get("Purchase Order")[0]]
|
||||
]
|
||||
|
||||
for jv_entry in new_jv.get("entries"):
|
||||
for jv_entry in new_jv.get("entries"):
|
||||
if paytool.party_account == jv_entry.get("account"):
|
||||
row = [
|
||||
jv_entry.get("account"),
|
||||
@@ -183,11 +185,11 @@ class TestPaymentTool(unittest.TestCase):
|
||||
jv_entry.get("against_purchase_order"),
|
||||
]
|
||||
self.assertTrue(row in expected_values)
|
||||
|
||||
|
||||
self.assertEquals(new_jv.get("cheque_no"), paytool.reference_no)
|
||||
self.assertEquals(new_jv.get("cheque_date"), paytool.reference_date)
|
||||
|
||||
def clear_table_entries(self):
|
||||
frappe.db.sql("""delete from `tabGL Entry` where (account = "_Test Customer 3 - _TC" or account = "_Test Supplier 1 - _TC")""")
|
||||
frappe.db.sql("""delete from `tabSales Order` where customer_name = "_Test Customer 3" """)
|
||||
frappe.db.sql("""delete from `tabPurchase Order` where supplier_name = "_Test Supplier 1" """)
|
||||
frappe.db.sql("""delete from `tabPurchase Order` where supplier_name = "_Test Supplier 1" """)
|
||||
|
||||
@@ -232,7 +232,6 @@ cur_frm.fields_dict['entries'].grid.get_field('project_name').get_query = functi
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cur_frm.cscript.select_print_heading = function(doc,cdt,cdn){
|
||||
if(doc.select_print_heading){
|
||||
// print heading
|
||||
|
||||
@@ -752,12 +752,133 @@
|
||||
"print_hide": 1,
|
||||
"read_only": 0,
|
||||
"reqd": 0
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.docstatus<2",
|
||||
"fieldname": "recurring_invoice",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Recurring Invoice",
|
||||
"options": "icon-time",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_77",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.docstatus<2",
|
||||
"description": "Check if recurring invoice, uncheck to stop recurring or put proper End Date",
|
||||
"fieldname": "is_recurring",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Recurring",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "Select the period when the invoice will be generated automatically",
|
||||
"fieldname": "recurring_type",
|
||||
"fieldtype": "Select",
|
||||
"label": "Recurring Type",
|
||||
"no_copy": 1,
|
||||
"options": "Monthly\nQuarterly\nHalf-yearly\nYearly",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "Start date of current invoice's period",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "From Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "End date of current invoice's period",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "To Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The day of the month on which auto invoice will be generated e.g. 05, 28 etc",
|
||||
"fieldname": "repeat_on_day_of_month",
|
||||
"fieldtype": "Int",
|
||||
"label": "Repeat on Day of Month",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The date on which recurring invoice will be stop",
|
||||
"fieldname": "end_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "End Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_82",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The date on which next invoice will be generated. It is generated on submit.",
|
||||
"fieldname": "next_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Next Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The unique id for tracking all recurring invoices. It is generated on submit.",
|
||||
"fieldname": "recurring_id",
|
||||
"fieldtype": "Data",
|
||||
"label": "Recurring Id",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "Enter email id separated by commas, invoice will be mailed automatically on particular date",
|
||||
"fieldname": "notification_email_address",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Notification Email Address",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1
|
||||
}
|
||||
],
|
||||
"icon": "icon-file-text",
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"modified": "2014-09-09 05:35:32.156763",
|
||||
"modified": "2014-10-08 14:23:20.234176",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice",
|
||||
|
||||
@@ -4,11 +4,10 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
from frappe.utils import cint, cstr, flt, formatdate
|
||||
|
||||
from frappe.utils import cint, cstr, formatdate, flt
|
||||
from frappe import msgprint, _, throw
|
||||
from erpnext.setup.utils import get_company_currency
|
||||
|
||||
import frappe.defaults
|
||||
|
||||
from erpnext.controllers.buying_controller import BuyingController
|
||||
@@ -49,6 +48,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.check_conversion_rate()
|
||||
self.validate_credit_acc()
|
||||
self.clear_unallocated_advances("Purchase Invoice Advance", "advance_allocation_details")
|
||||
self.validate_advance_jv("advance_allocation_details", "purchase_order")
|
||||
self.check_for_acc_head_of_supplier()
|
||||
self.check_for_stopped_status()
|
||||
self.validate_with_previous_doc()
|
||||
@@ -80,7 +80,7 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
def get_advances(self):
|
||||
super(PurchaseInvoice, self).get_advances(self.credit_to,
|
||||
"Purchase Invoice Advance", "advance_allocation_details", "debit")
|
||||
"Purchase Invoice Advance", "advance_allocation_details", "debit", "purchase_order")
|
||||
|
||||
def check_active_purchase_items(self):
|
||||
for d in self.get('entries'):
|
||||
@@ -249,6 +249,8 @@ class PurchaseInvoice(BuyingController):
|
||||
reconcile_against_document(lst)
|
||||
|
||||
def on_submit(self):
|
||||
super(PurchaseInvoice, self).on_submit()
|
||||
|
||||
self.check_prev_docstatus()
|
||||
|
||||
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
|
||||
|
||||
@@ -231,4 +231,8 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
self.assertTrue(not frappe.db.sql("""select name from `tabJournal Voucher Detail`
|
||||
where against_voucher=%s""", pi.name))
|
||||
|
||||
def test_recurring_invoice(self):
|
||||
from erpnext.controllers.tests.test_recurring_document import test_recurring_document
|
||||
test_recurring_document(self, test_records)
|
||||
|
||||
test_records = frappe.get_test_records('Purchase Invoice')
|
||||
|
||||
@@ -399,37 +399,6 @@ cur_frm.cscript.on_submit = function(doc, cdt, cdn) {
|
||||
})
|
||||
}
|
||||
|
||||
cur_frm.cscript.is_recurring = function(doc, dt, dn) {
|
||||
// set default values for recurring invoices
|
||||
if(doc.is_recurring) {
|
||||
var owner_email = doc.owner=="Administrator"
|
||||
? frappe.user_info("Administrator").email
|
||||
: doc.owner;
|
||||
|
||||
doc.notification_email_address = $.map([cstr(owner_email),
|
||||
cstr(doc.contact_email)], function(v) { return v || null; }).join(", ");
|
||||
doc.repeat_on_day_of_month = frappe.datetime.str_to_obj(doc.posting_date).getDate();
|
||||
}
|
||||
|
||||
refresh_many(["notification_email_address", "repeat_on_day_of_month"]);
|
||||
}
|
||||
|
||||
cur_frm.cscript.from_date = function(doc, dt, dn) {
|
||||
// set to_date
|
||||
if(doc.from_date) {
|
||||
var recurring_type_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6,
|
||||
'Yearly': 12};
|
||||
|
||||
var months = recurring_type_map[doc.recurring_type];
|
||||
if(months) {
|
||||
var to_date = frappe.datetime.add_months(doc.from_date,
|
||||
months);
|
||||
doc.to_date = frappe.datetime.add_days(to_date, -1);
|
||||
refresh_field('to_date');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript.send_sms = function() {
|
||||
frappe.require("assets/erpnext/js/sms_manager.js");
|
||||
var sms_man = new SMSManager(cur_frm.doc);
|
||||
|
||||
@@ -169,28 +169,25 @@
|
||||
"search_index": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "",
|
||||
"description": "Start date of current invoice's period",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "From",
|
||||
"no_copy": 1,
|
||||
"fieldname": "shipping_address_name",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
"in_filter": 1,
|
||||
"label": "Shipping Address Name",
|
||||
"options": "Address",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0
|
||||
"precision": "",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "",
|
||||
"description": "End date of current invoice's period",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "To",
|
||||
"no_copy": 1,
|
||||
"fieldname": "shipping_address",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"label": "Shipping Address",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "currency_section",
|
||||
@@ -1108,6 +1105,30 @@
|
||||
"print_hide": 1,
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "Start date of current invoice's period",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "From Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "End date of current invoice's period",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "To Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
@@ -1120,17 +1141,6 @@
|
||||
"print_hide": 1,
|
||||
"read_only": 0
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The date on which next invoice will be generated. It is generated on submit.\n",
|
||||
"fieldname": "next_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Next Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
@@ -1152,6 +1162,17 @@
|
||||
"read_only": 0,
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The date on which next invoice will be generated. It is generated on submit.\n",
|
||||
"fieldname": "next_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Next Date",
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The unique id for tracking all recurring invoices.\u00a0It is generated on submit.",
|
||||
@@ -1192,7 +1213,7 @@
|
||||
"icon": "icon-file-text",
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"modified": "2014-09-09 05:35:34.121045",
|
||||
"modified": "2014-10-10 16:54:22.284284",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
|
||||
@@ -4,18 +4,12 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import frappe.defaults
|
||||
|
||||
from frappe.utils import add_days, cint, cstr, date_diff, flt, getdate, nowdate, \
|
||||
get_first_day, get_last_day
|
||||
from frappe.model.naming import make_autoname
|
||||
from frappe.utils import cint, cstr, flt
|
||||
from frappe import _, msgprint, throw
|
||||
|
||||
from erpnext.accounts.party import get_party_account, get_due_date
|
||||
from erpnext.controllers.stock_controller import update_gl_entries_after
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
|
||||
from erpnext.controllers.recurring_document import *
|
||||
|
||||
from erpnext.controllers.selling_controller import SellingController
|
||||
|
||||
form_grid_templates = {
|
||||
@@ -56,6 +50,7 @@ class SalesInvoice(SellingController):
|
||||
self.validate_debit_acc()
|
||||
self.validate_fixed_asset_account()
|
||||
self.clear_unallocated_advances("Sales Invoice Advance", "advance_adjustment_details")
|
||||
self.validate_advance_jv("advance_adjustment_details", "sales_order")
|
||||
self.add_remarks()
|
||||
|
||||
if cint(self.is_pos):
|
||||
@@ -77,11 +72,12 @@ class SalesInvoice(SellingController):
|
||||
self.set_against_income_account()
|
||||
self.validate_c_form()
|
||||
self.validate_time_logs_are_submitted()
|
||||
validate_recurring_document(self)
|
||||
self.validate_multiple_billing("Delivery Note", "dn_detail", "amount",
|
||||
"delivery_note_details")
|
||||
|
||||
def on_submit(self):
|
||||
super(SalesInvoice, self).on_submit()
|
||||
|
||||
if cint(self.update_stock) == 1:
|
||||
self.update_stock_ledger()
|
||||
else:
|
||||
@@ -104,7 +100,6 @@ class SalesInvoice(SellingController):
|
||||
self.update_against_document_in_jv()
|
||||
|
||||
self.update_time_log_batch(self.name)
|
||||
convert_to_recurring(self, "RECINV.#####", self.posting_date)
|
||||
|
||||
def before_cancel(self):
|
||||
self.update_time_log_batch(None)
|
||||
@@ -145,14 +140,6 @@ class SalesInvoice(SellingController):
|
||||
'overflow_type': 'delivery'
|
||||
})
|
||||
|
||||
def on_update_after_submit(self):
|
||||
validate_recurring_document(self)
|
||||
convert_to_recurring(self, "RECINV.#####", self.posting_date)
|
||||
|
||||
def before_recurring(self):
|
||||
self.aging_date = None
|
||||
self.due_date = None
|
||||
|
||||
def get_portal_page(self):
|
||||
return "invoice" if self.docstatus==1 else None
|
||||
|
||||
@@ -222,7 +209,7 @@ class SalesInvoice(SellingController):
|
||||
|
||||
def get_advances(self):
|
||||
super(SalesInvoice, self).get_advances(self.debit_to,
|
||||
"Sales Invoice Advance", "advance_adjustment_details", "credit")
|
||||
"Sales Invoice Advance", "advance_adjustment_details", "credit", "sales_order")
|
||||
|
||||
def get_company_abbr(self):
|
||||
return frappe.db.sql("select abbr from tabCompany where name=%s", self.company)[0][0]
|
||||
@@ -486,9 +473,8 @@ class SalesInvoice(SellingController):
|
||||
|
||||
if repost_future_gle and cint(self.update_stock) \
|
||||
and cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
|
||||
items, warehouse_account = self.get_items_and_warehouse_accounts()
|
||||
update_gl_entries_after(self.posting_date, self.posting_time,
|
||||
warehouse_account, items)
|
||||
items, warehouses = self.get_items_and_warehouses()
|
||||
update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items)
|
||||
|
||||
def get_gl_entries(self, warehouse_account=None):
|
||||
from erpnext.accounts.general_ledger import merge_similar_entries
|
||||
|
||||
@@ -97,8 +97,7 @@ def validate_account_for_auto_accounting_for_stock(gl_map):
|
||||
|
||||
for entry in gl_map:
|
||||
if entry.account in aii_accounts:
|
||||
frappe.throw(_("Account: {0} can only be updated via \
|
||||
Stock Transactions").format(entry.account), StockAccountInvalidTransaction)
|
||||
frappe.throw(_("Account: {0} can only be updated via Stock Transactions").format(entry.account), StockAccountInvalidTransaction)
|
||||
|
||||
|
||||
def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
|
||||
|
||||
@@ -30,7 +30,8 @@ def execute(filters=None):
|
||||
data = []
|
||||
for gle in entries:
|
||||
if cstr(gle.against_voucher) == gle.voucher_no or not gle.against_voucher \
|
||||
or [gle.against_voucher_type, gle.against_voucher] in entries_after_report_date:
|
||||
or [gle.against_voucher_type, gle.against_voucher] in entries_after_report_date \
|
||||
or (gle.against_voucher_type == "Purchase Order"):
|
||||
voucher_details = voucher_detail_map.get(gle.voucher_type, {}).get(gle.voucher_no, {})
|
||||
|
||||
invoiced_amount = gle.credit > 0 and gle.credit or 0
|
||||
|
||||
@@ -21,7 +21,7 @@ class AccountsReceivableReport(object):
|
||||
def get_columns(self, customer_naming_by):
|
||||
columns = [
|
||||
_("Posting Date") + ":Date:80", _("Account") + ":Link/Account:150",
|
||||
_("Voucher Type") + "::110", _("Voucher No") + "::120", "::30",
|
||||
_("Voucher Type") + "::110", _("Voucher No") + ":Dynamic Link/Voucher Type:120",
|
||||
_("Due Date") + ":Date:80",
|
||||
_("Invoiced Amount") + ":Currency:100", _("Payment Received") + ":Currency:100",
|
||||
_("Outstanding Amount") + ":Currency:100", _("Age") + ":Int:50", "0-30:Currency:100",
|
||||
@@ -63,11 +63,6 @@ class AccountsReceivableReport(object):
|
||||
|
||||
row += [self.get_territory(gle.account), gle.remarks]
|
||||
data.append(row)
|
||||
|
||||
for i in range(0, len(data)):
|
||||
data[i].insert(4, """<a href="%s"><i class="icon icon-share" style="cursor: pointer;"></i></a>""" \
|
||||
% ("/".join(["#Form", data[i][2], data[i][3]]),))
|
||||
|
||||
return data
|
||||
|
||||
def get_entries_after(self, report_date):
|
||||
@@ -84,6 +79,9 @@ class AccountsReceivableReport(object):
|
||||
return (
|
||||
# advance
|
||||
(not gle.against_voucher) or
|
||||
|
||||
# against sales order
|
||||
(gle.against_voucher_type == "Sales Order") or
|
||||
|
||||
# sales invoice
|
||||
(gle.against_voucher==gle.voucher_no and gle.debit > 0) or
|
||||
|
||||
@@ -35,7 +35,7 @@ def validate_filters(filters, account_details):
|
||||
|
||||
def get_columns():
|
||||
return [_("Posting Date") + ":Date:100", _("Account") + ":Link/Account:200", _("Debit") + ":Float:100",
|
||||
_("Credit") + ":Float:100", _("Voucher Type") + "::120", _("Voucher No") + "::160", _("Link") + "::20",
|
||||
_("Credit") + ":Float:100", _("Voucher Type") + "::120", _("Voucher No") + ":Dynamic Link/Voucher Type:160",
|
||||
_("Against Account") + "::120", _("Cost Center") + ":Link/Cost Center:100", _("Remarks") + "::400"]
|
||||
|
||||
def get_result(filters, account_details):
|
||||
@@ -162,15 +162,6 @@ def get_result_as_list(data):
|
||||
for d in data:
|
||||
result.append([d.get("posting_date"), d.get("account"), d.get("debit"),
|
||||
d.get("credit"), d.get("voucher_type"), d.get("voucher_no"),
|
||||
get_voucher_link(d.get("voucher_type"), d.get("voucher_no")),
|
||||
d.get("against"), d.get("cost_center"), d.get("remarks")])
|
||||
|
||||
return result
|
||||
|
||||
def get_voucher_link(voucher_type, voucher_no):
|
||||
icon = ""
|
||||
if voucher_type and voucher_no:
|
||||
icon = """<a href="%s"><i class="icon icon-share" style="cursor: pointer;">
|
||||
</i></a>""" % ("/".join(["#Form", voucher_type, voucher_no]))
|
||||
|
||||
return icon
|
||||
|
||||
@@ -9,29 +9,29 @@ from erpnext.stock.utils import get_buying_amount, get_sales_bom_buying_amount
|
||||
|
||||
def execute(filters=None):
|
||||
if not filters: filters = {}
|
||||
|
||||
|
||||
stock_ledger_entries = get_stock_ledger_entries(filters)
|
||||
source = get_source_data(filters)
|
||||
item_sales_bom = get_item_sales_bom()
|
||||
|
||||
columns = [__("Delivery Note/Sales Invoice") + "::120", _("Link") + "::30", _("Posting Date") + ":Date", _("Posting Time"),
|
||||
|
||||
columns = [_("Delivery Note/Sales Invoice") + "::120", _("Link") + "::30", _("Posting Date") + ":Date", _("Posting Time"),
|
||||
_("Item Code") + ":Link/Item", _("Item Name"), _("Description"), _("Warehouse") + ":Link/Warehouse",
|
||||
_("Qty") + ":Float", _("Selling Rate") + ":Currency", _("Avg. Buying Rate") + ":Currency",
|
||||
_("Qty") + ":Float", _("Selling Rate") + ":Currency", _("Avg. Buying Rate") + ":Currency",
|
||||
_("Selling Amount") + ":Currency", _("Buying Amount") + ":Currency",
|
||||
_("Gross Profit") + ":Currency", _("Gross Profit %") + ":Percent", _("Project") + ":Link/Project"]
|
||||
data = []
|
||||
for row in source:
|
||||
selling_amount = flt(row.base_amount)
|
||||
|
||||
|
||||
item_sales_bom_map = item_sales_bom.get(row.parenttype, {}).get(row.name, frappe._dict())
|
||||
|
||||
|
||||
if item_sales_bom_map.get(row.item_code):
|
||||
buying_amount = get_sales_bom_buying_amount(row.item_code, row.warehouse,
|
||||
buying_amount = get_sales_bom_buying_amount(row.item_code, row.warehouse,
|
||||
row.parenttype, row.name, row.item_row, stock_ledger_entries, item_sales_bom_map)
|
||||
else:
|
||||
buying_amount = get_buying_amount(row.parenttype, row.name, row.item_row,
|
||||
stock_ledger_entries.get((row.item_code, row.warehouse), []))
|
||||
|
||||
|
||||
buying_amount = buying_amount > 0 and buying_amount or 0
|
||||
|
||||
gross_profit = selling_amount - buying_amount
|
||||
@@ -39,41 +39,41 @@ def execute(filters=None):
|
||||
gross_profit_percent = (gross_profit / selling_amount) * 100.0
|
||||
else:
|
||||
gross_profit_percent = 0.0
|
||||
|
||||
|
||||
icon = """<a href="%s"><i class="icon icon-share" style="cursor: pointer;"></i></a>""" \
|
||||
% ("/".join(["#Form", row.parenttype, row.name]),)
|
||||
data.append([row.name, icon, row.posting_date, row.posting_time, row.item_code, row.item_name,
|
||||
row.description, row.warehouse, row.qty, row.base_rate,
|
||||
row.description, row.warehouse, row.qty, row.base_rate,
|
||||
row.qty and (buying_amount / row.qty) or 0, row.base_amount, buying_amount,
|
||||
gross_profit, gross_profit_percent, row.project])
|
||||
|
||||
|
||||
return columns, data
|
||||
|
||||
def get_stock_ledger_entries(filters):
|
||||
|
||||
def get_stock_ledger_entries(filters):
|
||||
query = """select item_code, voucher_type, voucher_no,
|
||||
voucher_detail_no, posting_date, posting_time, stock_value,
|
||||
warehouse, actual_qty as qty
|
||||
from `tabStock Ledger Entry`"""
|
||||
|
||||
|
||||
if filters.get("company"):
|
||||
query += """ where company=%(company)s"""
|
||||
|
||||
|
||||
query += " order by item_code desc, warehouse desc, posting_date desc, posting_time desc, name desc"
|
||||
|
||||
|
||||
res = frappe.db.sql(query, filters, as_dict=True)
|
||||
|
||||
|
||||
out = {}
|
||||
for r in res:
|
||||
if (r.item_code, r.warehouse) not in out:
|
||||
out[(r.item_code, r.warehouse)] = []
|
||||
|
||||
|
||||
out[(r.item_code, r.warehouse)].append(r)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def get_item_sales_bom():
|
||||
item_sales_bom = {}
|
||||
|
||||
|
||||
for d in frappe.db.sql("""select parenttype, parent, parent_item,
|
||||
item_code, warehouse, -1*qty as total_qty, parent_detail_docname
|
||||
from `tabPacked Item` where docstatus=1""", as_dict=True):
|
||||
@@ -81,7 +81,7 @@ def get_item_sales_bom():
|
||||
frappe._dict()).setdefault(d.parent_item, []).append(d)
|
||||
|
||||
return item_sales_bom
|
||||
|
||||
|
||||
def get_source_data(filters):
|
||||
conditions = ""
|
||||
if filters.get("company"):
|
||||
@@ -90,9 +90,9 @@ def get_source_data(filters):
|
||||
conditions += " and posting_date>=%(from_date)s"
|
||||
if filters.get("to_date"):
|
||||
conditions += " and posting_date<=%(to_date)s"
|
||||
|
||||
delivery_note_items = frappe.db.sql("""select item.parenttype, dn.name,
|
||||
dn.posting_date, dn.posting_time, dn.project_name,
|
||||
|
||||
delivery_note_items = frappe.db.sql("""select item.parenttype, dn.name,
|
||||
dn.posting_date, dn.posting_time, dn.project_name,
|
||||
item.item_code, item.item_name, item.description, item.warehouse,
|
||||
item.qty, item.base_rate, item.base_amount, item.name as "item_row",
|
||||
timestamp(dn.posting_date, dn.posting_time) as posting_datetime
|
||||
@@ -100,7 +100,7 @@ def get_source_data(filters):
|
||||
where item.parent = dn.name and dn.docstatus = 1 %s
|
||||
order by dn.posting_date desc, dn.posting_time desc""" % (conditions,), filters, as_dict=1)
|
||||
|
||||
sales_invoice_items = frappe.db.sql("""select item.parenttype, si.name,
|
||||
sales_invoice_items = frappe.db.sql("""select item.parenttype, si.name,
|
||||
si.posting_date, si.posting_time, si.project_name,
|
||||
item.item_code, item.item_name, item.description, item.warehouse,
|
||||
item.qty, item.base_rate, item.base_amount, item.name as "item_row",
|
||||
@@ -109,9 +109,9 @@ def get_source_data(filters):
|
||||
where item.parent = si.name and si.docstatus = 1 %s
|
||||
and si.update_stock = 1
|
||||
order by si.posting_date desc, si.posting_time desc""" % (conditions,), filters, as_dict=1)
|
||||
|
||||
|
||||
source = delivery_note_items + sales_invoice_items
|
||||
if len(source) > len(delivery_note_items):
|
||||
source.sort(key=lambda d: d.posting_datetime, reverse=True)
|
||||
|
||||
return source
|
||||
|
||||
return source
|
||||
|
||||
@@ -11,4 +11,4 @@ def execute(filters=None):
|
||||
conditions = get_columns(filters, "Sales Invoice")
|
||||
data = get_data(filters, conditions)
|
||||
|
||||
return conditions["columns"], data
|
||||
return conditions["columns"], data
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -162,6 +162,8 @@ class PurchaseOrder(BuyingController):
|
||||
msgprint(_("Status of {0} {1} is now {2}").format(self.doctype, self.name, status))
|
||||
|
||||
def on_submit(self):
|
||||
super(PurchaseOrder, self).on_submit()
|
||||
|
||||
purchase_controller = frappe.get_doc("Purchase Common")
|
||||
|
||||
self.update_prevdoc_status()
|
||||
|
||||
@@ -107,7 +107,11 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
po.get("po_details")[0].qty = 3.4
|
||||
self.assertRaises(UOMMustBeIntegerError, po.insert)
|
||||
|
||||
def test_recurring_order(self):
|
||||
from erpnext.controllers.tests.test_recurring_document import test_recurring_document
|
||||
test_recurring_document(self, test_records)
|
||||
|
||||
test_dependencies = ["BOM"]
|
||||
|
||||
test_dependencies = ["BOM", "Item Price"]
|
||||
|
||||
test_records = frappe.get_test_records('Purchase Order')
|
||||
|
||||
@@ -26,6 +26,11 @@ def get_data():
|
||||
"name": "Purchase Receipt",
|
||||
"description": _("Goods received from Suppliers."),
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Installation Note",
|
||||
"description": _("Installation record for a Serial No.")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Item",
|
||||
@@ -57,11 +62,6 @@ def get_data():
|
||||
"name": "Stock Reconciliation",
|
||||
"description": _("Upload stock balance via csv.")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Installation Note",
|
||||
"description": _("Installation record for a Serial No.")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Packing Slip",
|
||||
@@ -142,10 +142,10 @@ def get_data():
|
||||
"doctype": "Item",
|
||||
},
|
||||
{
|
||||
"type": "page",
|
||||
"name": "stock-balance",
|
||||
"label": _("Stock Balance"),
|
||||
"icon": "icon-table",
|
||||
"type": "report",
|
||||
"is_query_report": True,
|
||||
"name": "Stock Balance",
|
||||
"doctype": "Warehouse"
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
@@ -170,13 +170,7 @@ def get_data():
|
||||
"name": "stock-analytics",
|
||||
"label": _("Stock Analytics"),
|
||||
"icon": "icon-bar-chart"
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"is_query_report": True,
|
||||
"name": "Warehouse-Wise Stock Balance",
|
||||
"doctype": "Warehouse"
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -4,12 +4,11 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _, throw
|
||||
from frappe.utils import add_days, cint, cstr, today, date_diff, flt, getdate, nowdate, \
|
||||
get_first_day, get_last_day
|
||||
from frappe.model.naming import make_autoname
|
||||
from frappe.utils import cint, today, flt
|
||||
from erpnext.setup.utils import get_company_currency, get_exchange_rate
|
||||
from erpnext.accounts.utils import get_fiscal_year, validate_fiscal_year
|
||||
from erpnext.utilities.transaction_base import TransactionBase
|
||||
from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document
|
||||
import json
|
||||
|
||||
class AccountsController(TransactionBase):
|
||||
@@ -24,6 +23,24 @@ class AccountsController(TransactionBase):
|
||||
|
||||
self.validate_for_freezed_account()
|
||||
|
||||
if self.meta.get_field("is_recurring"):
|
||||
validate_recurring_document(self)
|
||||
|
||||
def on_submit(self):
|
||||
if self.meta.get_field("is_recurring"):
|
||||
convert_to_recurring(self, self.get("posting_date") or self.get("transaction_date"))
|
||||
|
||||
def on_update_after_submit(self):
|
||||
if self.meta.get_field("is_recurring"):
|
||||
validate_recurring_document(self)
|
||||
convert_to_recurring(self, self.get("posting_date") or self.get("transaction_date"))
|
||||
|
||||
def before_recurring(self):
|
||||
self.fiscal_year = None
|
||||
for fieldname in ("due_date", "aging_date"):
|
||||
if self.meta.get_field(fieldname):
|
||||
self.set(fieldname, None)
|
||||
|
||||
def set_missing_values(self, for_validate=False):
|
||||
for fieldname in ["posting_date", "transaction_date"]:
|
||||
if not self.get(fieldname) and self.meta.get_field(fieldname):
|
||||
@@ -362,38 +379,67 @@ class AccountsController(TransactionBase):
|
||||
frappe.db.sql("""delete from `tab%s` where parentfield=%s and parent = %s
|
||||
and ifnull(allocated_amount, 0) = 0""" % (childtype, '%s', '%s'), (parentfield, self.name))
|
||||
|
||||
def get_advances(self, account_head, child_doctype, parentfield, dr_or_cr):
|
||||
against_order_list = []
|
||||
def get_advances(self, account_head, child_doctype, parentfield, dr_or_cr, against_order_field):
|
||||
so_list = list(set([d.get(against_order_field) for d in self.get("entries") if d.get(against_order_field)]))
|
||||
cond = ""
|
||||
if so_list:
|
||||
cond = "or (ifnull(t2.%s, '') in (%s))" % ("against_" + against_order_field, ', '.join(['%s']*len(so_list)))
|
||||
|
||||
res = frappe.db.sql("""
|
||||
select
|
||||
t1.name as jv_no, t1.remark, t2.%s as amount, t2.name as jv_detail_no, t2.%s as order_no
|
||||
t1.name as jv_no, t1.remark, t2.%s as amount, t2.name as jv_detail_no, `against_%s` as against_order
|
||||
from
|
||||
`tabJournal Voucher` t1, `tabJournal Voucher Detail` t2
|
||||
where
|
||||
t1.name = t2.parent and t2.account = %s and t2.is_advance = 'Yes' and t1.docstatus = 1
|
||||
and ifnull(t2.against_voucher, '') = ''
|
||||
and ifnull(t2.against_invoice, '') = ''
|
||||
and ifnull(t2.against_jv, '') = ''
|
||||
and ((
|
||||
ifnull(t2.against_voucher, '') = ''
|
||||
and ifnull(t2.against_invoice, '') = ''
|
||||
and ifnull(t2.against_jv, '') = ''
|
||||
and ifnull(t2.against_sales_order, '') = ''
|
||||
and ifnull(t2.against_purchase_order, '') = ''
|
||||
) %s)
|
||||
order by t1.posting_date""" %
|
||||
(dr_or_cr, "against_sales_order" if dr_or_cr == "credit" \
|
||||
else "against_purchase_order", '%s'),
|
||||
account_head, as_dict= True)
|
||||
|
||||
if self.get("entries"):
|
||||
for i in self.get("entries"):
|
||||
against_order_list.append(i.sales_order if dr_or_cr == "credit" else i.purchase_order)
|
||||
(dr_or_cr, against_order_field, '%s', cond),
|
||||
tuple([account_head] + so_list), as_dict= True)
|
||||
|
||||
self.set(parentfield, [])
|
||||
for d in res:
|
||||
if not against_order_list or d.order_no in against_order_list:
|
||||
self.append(parentfield, {
|
||||
"doctype": child_doctype,
|
||||
"journal_voucher": d.jv_no,
|
||||
"jv_detail_no": d.jv_detail_no,
|
||||
"remarks": d.remark,
|
||||
"advance_amount": flt(d.amount),
|
||||
"allocate_amount": 0
|
||||
})
|
||||
self.append(parentfield, {
|
||||
"doctype": child_doctype,
|
||||
"journal_voucher": d.jv_no,
|
||||
"jv_detail_no": d.jv_detail_no,
|
||||
"remarks": d.remark,
|
||||
"advance_amount": flt(d.amount),
|
||||
"allocated_amount": flt(d.amount) if d.against_order else 0
|
||||
})
|
||||
|
||||
def validate_advance_jv(self, advance_table_fieldname, against_order_field):
|
||||
order_list = list(set([d.get(against_order_field) for d in self.get("entries") if d.get(against_order_field)]))
|
||||
if order_list:
|
||||
account = self.get("debit_to" if self.doctype=="Sales Invoice" else "credit_to")
|
||||
|
||||
jv_against_order = frappe.db.sql("""select parent, %s as against_order
|
||||
from `tabJournal Voucher Detail`
|
||||
where docstatus=1 and account=%s and ifnull(is_advance, 'No') = 'Yes'
|
||||
and ifnull(against_sales_order, '') in (%s)
|
||||
group by parent, against_sales_order""" %
|
||||
("against_" + against_order_field, '%s', ', '.join(['%s']*len(order_list))),
|
||||
tuple([account] + order_list), as_dict=1)
|
||||
|
||||
if jv_against_order:
|
||||
order_jv_map = {}
|
||||
for d in jv_against_order:
|
||||
order_jv_map.setdefault(d.against_order, []).append(d.parent)
|
||||
|
||||
advance_jv_against_si = [d.journal_voucher for d in self.get(advance_table_fieldname)]
|
||||
|
||||
for order, jv_list in order_jv_map.items():
|
||||
for jv in jv_list:
|
||||
if not advance_jv_against_si or jv not in advance_jv_against_si:
|
||||
frappe.throw(_("Journal Voucher {0} is linked against Order {1}, hence it must be fetched as advance in Invoice as well.")
|
||||
.format(jv, order))
|
||||
|
||||
|
||||
def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield):
|
||||
from erpnext.controllers.status_updater import get_tolerance_for
|
||||
@@ -421,7 +467,6 @@ class AccountsController(TransactionBase):
|
||||
max_allowed_amt = flt(ref_amt * (100 + tolerance) / 100)
|
||||
|
||||
if total_billed_amt - max_allowed_amt > 0.01:
|
||||
reduce_by = total_billed_amt - max_allowed_amt
|
||||
frappe.throw(_("Cannot overbill for Item {0} in row {0} more than {1}. To allow overbilling, please set in Stock Settings").format(item.item_code, item.idx, max_allowed_amt))
|
||||
|
||||
def get_company_default(self, fieldname):
|
||||
|
||||
@@ -5,6 +5,7 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _, msgprint
|
||||
from frappe.utils import flt, rounded
|
||||
|
||||
from erpnext.setup.utils import get_company_currency
|
||||
from erpnext.accounts.party import get_party_details
|
||||
|
||||
@@ -255,8 +256,6 @@ class BuyingController(StockController):
|
||||
rm.required_qty = required_qty
|
||||
|
||||
rm.conversion_factor = item.conversion_factor
|
||||
rm.rate = bom_item.rate
|
||||
rm.amount = required_qty * flt(bom_item.rate)
|
||||
rm.idx = rm_supplied_idx
|
||||
|
||||
if self.doctype == "Purchase Receipt":
|
||||
@@ -267,7 +266,25 @@ class BuyingController(StockController):
|
||||
|
||||
rm_supplied_idx += 1
|
||||
|
||||
raw_materials_cost += required_qty * flt(bom_item.rate)
|
||||
# get raw materials rate
|
||||
if self.doctype == "Purchase Receipt":
|
||||
from erpnext.stock.utils import get_incoming_rate
|
||||
rm.rate = get_incoming_rate({
|
||||
"item_code": bom_item.item_code,
|
||||
"warehouse": self.supplier_warehouse,
|
||||
"posting_date": self.posting_date,
|
||||
"posting_time": self.posting_time,
|
||||
"qty": -1 * required_qty,
|
||||
"serial_no": rm.serial_no
|
||||
})
|
||||
if not rm.rate:
|
||||
from erpnext.stock.stock_ledger import get_valuation_rate
|
||||
rm.rate = get_valuation_rate(bom_item.item_code, self.supplier_warehouse)
|
||||
else:
|
||||
rm.rate = bom_item.rate
|
||||
|
||||
rm.amount = required_qty * flt(rm.rate)
|
||||
raw_materials_cost += flt(rm.amount)
|
||||
|
||||
if self.doctype == "Purchase Receipt":
|
||||
item.rm_supp_cost = raw_materials_cost
|
||||
|
||||
@@ -2,15 +2,28 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
import frappe.utils
|
||||
import frappe.defaults
|
||||
from frappe.utils import cint, cstr, getdate, nowdate, get_first_day, get_last_day
|
||||
|
||||
from frappe.utils import add_days, cint, cstr, date_diff, flt, getdate, nowdate, \
|
||||
get_first_day, get_last_day, comma_and
|
||||
from frappe.model.naming import make_autoname
|
||||
|
||||
from frappe import _, msgprint, throw
|
||||
from erpnext.accounts.party import get_party_account, get_due_date, get_party_details
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
|
||||
month_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6, 'Yearly': 12}
|
||||
date_field_map = {
|
||||
"Sales Order": "transaction_date",
|
||||
"Sales Invoice": "posting_date",
|
||||
"Purchase Order": "transaction_date",
|
||||
"Purchase Invoice": "posting_date"
|
||||
}
|
||||
|
||||
def create_recurring_documents():
|
||||
manage_recurring_documents("Sales Order")
|
||||
manage_recurring_documents("Sales Invoice")
|
||||
manage_recurring_documents("Purchase Order")
|
||||
manage_recurring_documents("Purchase Invoice")
|
||||
|
||||
def manage_recurring_documents(doctype, next_date=None, commit=True):
|
||||
"""
|
||||
@@ -19,10 +32,7 @@ def manage_recurring_documents(doctype, next_date=None, commit=True):
|
||||
"""
|
||||
next_date = next_date or nowdate()
|
||||
|
||||
if doctype == "Sales Order":
|
||||
date_field = "transaction_date"
|
||||
elif doctype == "Sales Invoice":
|
||||
date_field = "posting_date"
|
||||
date_field = date_field_map[doctype]
|
||||
|
||||
recurring_documents = frappe.db.sql("""select name, recurring_id
|
||||
from `tab{}` where ifnull(is_recurring, 0)=1
|
||||
@@ -51,7 +61,8 @@ def manage_recurring_documents(doctype, next_date=None, commit=True):
|
||||
frappe.db.sql("update `tab%s` \
|
||||
set is_recurring = 0 where name = %s" % (doctype, '%s'),
|
||||
(ref_document))
|
||||
notify_errors(ref_document, doctype, ref_wrapper.customer, ref_wrapper.owner)
|
||||
notify_errors(ref_document, doctype, ref_wrapper.get("customer") or ref_wrapper.get("supplier"),
|
||||
ref_wrapper.owner)
|
||||
frappe.db.commit()
|
||||
|
||||
exception_list.append(frappe.get_traceback())
|
||||
@@ -118,7 +129,7 @@ def send_notification(new_rv):
|
||||
"fcontent": frappe.get_print_format(new_rv.doctype, new_rv.name, as_pdf=True)
|
||||
}])
|
||||
|
||||
def notify_errors(doc, doctype, customer, owner):
|
||||
def notify_errors(doc, doctype, party, owner):
|
||||
from frappe.utils.user import get_system_managers
|
||||
recipients = get_system_managers(only_name=True)
|
||||
|
||||
@@ -127,7 +138,7 @@ def notify_errors(doc, doctype, customer, owner):
|
||||
message = frappe.get_template("templates/emails/recurring_document_failed.html").render({
|
||||
"type": doctype,
|
||||
"name": doc,
|
||||
"customer": customer
|
||||
"party": party
|
||||
}))
|
||||
|
||||
assign_task_to_owner(doc, doctype, "Recurring Invoice Failed", recipients)
|
||||
@@ -155,18 +166,18 @@ def validate_recurring_document(doc):
|
||||
elif not (doc.from_date and doc.to_date):
|
||||
throw(_("Period From and Period To dates mandatory for recurring %s") % doc.doctype)
|
||||
|
||||
def convert_to_recurring(doc, autoname, posting_date):
|
||||
if doc.is_recurring:
|
||||
if not doc.recurring_id:
|
||||
frappe.db.set(doc, "recurring_id",
|
||||
make_autoname(autoname))
|
||||
#
|
||||
def convert_to_recurring(doc, posting_date):
|
||||
if doc.is_recurring:
|
||||
if not doc.recurring_id:
|
||||
frappe.db.set(doc, "recurring_id", doc.name)
|
||||
|
||||
set_next_date(doc, posting_date)
|
||||
set_next_date(doc, posting_date)
|
||||
|
||||
elif doc.recurring_id:
|
||||
frappe.db.sql("""update `tab%s`
|
||||
set is_recurring = 0
|
||||
where recurring_id = %s""" % (doc.doctype, '%s'), (doc.recurring_id))
|
||||
elif doc.recurring_id:
|
||||
frappe.db.sql("""update `tab%s` set is_recurring = 0
|
||||
where recurring_id = %s""" % (doc.doctype, '%s'), (doc.recurring_id))
|
||||
#
|
||||
|
||||
def validate_notification_email_id(doc):
|
||||
if doc.notification_email_address:
|
||||
|
||||
@@ -8,7 +8,7 @@ from frappe import msgprint, _
|
||||
import frappe.defaults
|
||||
|
||||
from erpnext.controllers.accounts_controller import AccountsController
|
||||
from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries
|
||||
from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries, process_gl_map
|
||||
|
||||
class StockController(AccountsController):
|
||||
def make_gl_entries(self, repost_future_gle=True):
|
||||
@@ -16,20 +16,20 @@ class StockController(AccountsController):
|
||||
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
|
||||
|
||||
if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
|
||||
warehouse_account = self.get_warehouse_account()
|
||||
warehouse_account = get_warehouse_account()
|
||||
|
||||
if self.docstatus==1:
|
||||
gl_entries = self.get_gl_entries(warehouse_account)
|
||||
make_gl_entries(gl_entries)
|
||||
|
||||
if repost_future_gle:
|
||||
items, warehouse_account = self.get_items_and_warehouse_accounts(warehouse_account)
|
||||
update_gl_entries_after(self.posting_date, self.posting_time,
|
||||
warehouse_account, items)
|
||||
items, warehouses = self.get_items_and_warehouses()
|
||||
update_gl_entries_after(self.posting_date, self.posting_time, warehouses, items,
|
||||
warehouse_account)
|
||||
|
||||
def get_gl_entries(self, warehouse_account=None, default_expense_account=None,
|
||||
default_cost_center=None):
|
||||
from erpnext.accounts.general_ledger import process_gl_map
|
||||
|
||||
if not warehouse_account:
|
||||
warehouse_account = get_warehouse_account()
|
||||
|
||||
@@ -88,10 +88,8 @@ class StockController(AccountsController):
|
||||
|
||||
return details
|
||||
|
||||
def get_items_and_warehouse_accounts(self, warehouse_account=None):
|
||||
def get_items_and_warehouses(self):
|
||||
items, warehouses = [], []
|
||||
if not warehouse_account:
|
||||
warehouse_account = get_warehouse_account()
|
||||
|
||||
if hasattr(self, "fname"):
|
||||
item_doclist = self.get(self.fname)
|
||||
@@ -117,86 +115,17 @@ class StockController(AccountsController):
|
||||
if d.get("t_warehouse") and d.t_warehouse not in warehouses:
|
||||
warehouses.append(d.t_warehouse)
|
||||
|
||||
warehouse_account = {wh: warehouse_account[wh] for wh in warehouses
|
||||
if warehouse_account.get(wh)}
|
||||
|
||||
return items, warehouse_account
|
||||
return items, warehouses
|
||||
|
||||
def get_stock_ledger_details(self):
|
||||
stock_ledger = {}
|
||||
for sle in frappe.db.sql("""select warehouse, stock_value_difference, voucher_detail_no
|
||||
for sle in frappe.db.sql("""select warehouse, stock_value_difference,
|
||||
voucher_detail_no, item_code, posting_date, actual_qty
|
||||
from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""",
|
||||
(self.doctype, self.name), as_dict=True):
|
||||
stock_ledger.setdefault(sle.voucher_detail_no, []).append(sle)
|
||||
return stock_ledger
|
||||
|
||||
def get_warehouse_account(self):
|
||||
warehouse_account = dict(frappe.db.sql("""select master_name, name from tabAccount
|
||||
where account_type = 'Warehouse' and ifnull(master_name, '') != ''"""))
|
||||
return warehouse_account
|
||||
|
||||
def update_gl_entries_after(self, warehouse_account=None):
|
||||
future_stock_vouchers = self.get_future_stock_vouchers()
|
||||
gle = self.get_voucherwise_gl_entries(future_stock_vouchers)
|
||||
if not warehouse_account:
|
||||
warehouse_account = self.get_warehouse_account()
|
||||
for voucher_type, voucher_no in future_stock_vouchers:
|
||||
existing_gle = gle.get((voucher_type, voucher_no), [])
|
||||
voucher_obj = frappe.get_doc(voucher_type, voucher_no)
|
||||
expected_gle = voucher_obj.get_gl_entries(warehouse_account)
|
||||
if expected_gle:
|
||||
matched = True
|
||||
if existing_gle:
|
||||
for entry in expected_gle:
|
||||
for e in existing_gle:
|
||||
if entry.account==e.account \
|
||||
and entry.against_account==e.against_account\
|
||||
and entry.cost_center==e.cost_center:
|
||||
if entry.debit != e.debit or entry.credit != e.credit:
|
||||
matched = False
|
||||
break
|
||||
else:
|
||||
matched = False
|
||||
|
||||
if not matched:
|
||||
self.delete_gl_entries(voucher_type, voucher_no)
|
||||
voucher_obj.make_gl_entries(repost_future_gle=False)
|
||||
else:
|
||||
self.delete_gl_entries(voucher_type, voucher_no)
|
||||
|
||||
|
||||
def get_future_stock_vouchers(self):
|
||||
condition = ""
|
||||
item_list = []
|
||||
if getattr(self, "fname", None):
|
||||
item_list = [d.item_code for d in self.get(self.fname)]
|
||||
if item_list:
|
||||
condition = "and item_code in ({})".format(", ".join(["%s"] * len(item_list)))
|
||||
|
||||
future_stock_vouchers = frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
|
||||
from `tabStock Ledger Entry` sle
|
||||
where timestamp(sle.posting_date, sle.posting_time) >= timestamp(%s, %s) {condition}
|
||||
order by timestamp(sle.posting_date, sle.posting_time) asc, name asc""".format(
|
||||
condition=condition), tuple([self.posting_date, self.posting_date] + item_list),
|
||||
as_list=True)
|
||||
|
||||
return future_stock_vouchers
|
||||
|
||||
def get_voucherwise_gl_entries(self, future_stock_vouchers):
|
||||
gl_entries = {}
|
||||
if future_stock_vouchers:
|
||||
for d in frappe.db.sql("""select * from `tabGL Entry`
|
||||
where posting_date >= %s and voucher_no in (%s)""" %
|
||||
('%s', ', '.join(['%s']*len(future_stock_vouchers))),
|
||||
tuple([self.posting_date] + [d[1] for d in future_stock_vouchers]), as_dict=1):
|
||||
gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d)
|
||||
|
||||
return gl_entries
|
||||
|
||||
def delete_gl_entries(self, voucher_type, voucher_no):
|
||||
frappe.db.sql("""delete from `tabGL Entry`
|
||||
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
|
||||
|
||||
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]
|
||||
@@ -287,15 +216,16 @@ class StockController(AccountsController):
|
||||
|
||||
return serialized_items
|
||||
|
||||
def update_gl_entries_after(posting_date, posting_time, warehouse_account=None, for_items=None):
|
||||
def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
|
||||
warehouse_account=None):
|
||||
def _delete_gl_entries(voucher_type, voucher_no):
|
||||
frappe.db.sql("""delete from `tabGL Entry`
|
||||
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
|
||||
|
||||
if not warehouse_account:
|
||||
warehouse_account = get_warehouse_account()
|
||||
future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time,
|
||||
warehouse_account, for_items)
|
||||
|
||||
future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items)
|
||||
gle = get_voucherwise_gl_entries(future_stock_vouchers, posting_date)
|
||||
|
||||
for voucher_type, voucher_no in future_stock_vouchers:
|
||||
@@ -321,7 +251,7 @@ def compare_existing_and_expected_gle(existing_gle, expected_gle):
|
||||
break
|
||||
return matched
|
||||
|
||||
def get_future_stock_vouchers(posting_date, posting_time, warehouse_account=None, for_items=None):
|
||||
def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, for_items=None):
|
||||
future_stock_vouchers = []
|
||||
|
||||
values = []
|
||||
@@ -330,9 +260,9 @@ def get_future_stock_vouchers(posting_date, posting_time, warehouse_account=None
|
||||
condition += " and item_code in ({})".format(", ".join(["%s"] * len(for_items)))
|
||||
values += for_items
|
||||
|
||||
if warehouse_account:
|
||||
condition += " and warehouse in ({})".format(", ".join(["%s"] * len(warehouse_account.keys())))
|
||||
values += warehouse_account.keys()
|
||||
if for_warehouses:
|
||||
condition += " and warehouse in ({})".format(", ".join(["%s"] * len(for_warehouses)))
|
||||
values += for_warehouses
|
||||
|
||||
for d in frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
|
||||
from `tabStock Ledger Entry` sle
|
||||
|
||||
@@ -2,12 +2,8 @@
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
import frappe
|
||||
import unittest, json, copy
|
||||
from frappe.utils import flt
|
||||
import frappe.permissions
|
||||
from erpnext.accounts.utils import get_stock_and_account_difference
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
|
||||
from erpnext.projects.doctype.time_log_batch.test_time_log_batch import *
|
||||
from erpnext.controllers.recurring_document import date_field_map
|
||||
|
||||
def test_recurring_document(obj, test_records):
|
||||
from frappe.utils import get_first_day, get_last_day, add_to_date, nowdate, getdate, add_days
|
||||
@@ -27,20 +23,11 @@ def test_recurring_document(obj, test_records):
|
||||
"to_date": get_last_day(today)
|
||||
})
|
||||
|
||||
if base_doc.doctype == "Sales Order":
|
||||
base_doc.update({
|
||||
"transaction_date": today,
|
||||
"delivery_date": add_days(today, 15)
|
||||
})
|
||||
elif base_doc.doctype == "Sales Invoice":
|
||||
base_doc.update({
|
||||
"posting_date": today
|
||||
})
|
||||
date_field = date_field_map[base_doc.doctype]
|
||||
base_doc.set(date_field, today)
|
||||
|
||||
if base_doc.doctype == "Sales Order":
|
||||
date_field = "transaction_date"
|
||||
elif base_doc.doctype == "Sales Invoice":
|
||||
date_field = "posting_date"
|
||||
base_doc.set("delivery_date", add_days(today, 15))
|
||||
|
||||
# monthly
|
||||
doc1 = frappe.copy_doc(base_doc)
|
||||
@@ -128,7 +115,7 @@ def _test_recurring_document(obj, base_doc, date_field, first_and_last_day):
|
||||
|
||||
next_date = get_next_date(base_doc.get(date_field), no_of_months,
|
||||
base_doc.repeat_on_day_of_month)
|
||||
|
||||
|
||||
manage_recurring_documents(base_doc.doctype, next_date=next_date, commit=False)
|
||||
|
||||
recurred_documents = frappe.db.sql("""select name from `tab%s`
|
||||
|
||||
@@ -4,7 +4,7 @@ app_publisher = "Web Notes Technologies Pvt. Ltd. and Contributors"
|
||||
app_description = "Open Source Enterprise Resource Planning for Small and Midsized Organizations"
|
||||
app_icon = "icon-th"
|
||||
app_color = "#e74c3c"
|
||||
app_version = "4.4.0"
|
||||
app_version = "4.7.0"
|
||||
|
||||
error_report_email = "support@erpnext.com"
|
||||
|
||||
|
||||
@@ -115,6 +115,9 @@ class BOM(Document):
|
||||
return rate
|
||||
|
||||
def update_cost(self):
|
||||
if self.docstatus == 2:
|
||||
return
|
||||
|
||||
for d in self.get("bom_materials"):
|
||||
d.rate = self.get_bom_material_detail({
|
||||
'item_code': d.item_code,
|
||||
@@ -122,9 +125,10 @@ class BOM(Document):
|
||||
'qty': d.qty
|
||||
})["rate"]
|
||||
|
||||
if self.docstatus in (0, 1):
|
||||
if self.docstatus == 1:
|
||||
self.ignore_validate_update_after_submit = True
|
||||
self.save()
|
||||
self.calculate_cost()
|
||||
self.save()
|
||||
|
||||
def get_bom_unitcost(self, bom_no):
|
||||
bom = frappe.db.sql("""select name, total_variable_cost/quantity as unit_cost from `tabBOM`
|
||||
@@ -269,29 +273,27 @@ class BOM(Document):
|
||||
"""Calculate bom totals"""
|
||||
self.calculate_op_cost()
|
||||
self.calculate_rm_cost()
|
||||
self.calculate_fixed_cost()
|
||||
self.total_variable_cost = self.raw_material_cost + self.operating_cost
|
||||
self.total_cost = self.total_variable_cost + self.total_fixed_cost
|
||||
|
||||
def calculate_op_cost(self):
|
||||
"""Update workstation rate and calculates totals"""
|
||||
total_op_cost = 0
|
||||
total_op_cost, fixed_cost = 0, 0
|
||||
for d in self.get('bom_operations'):
|
||||
if d.workstation and not d.hour_rate:
|
||||
d.hour_rate = frappe.db.get_value("Workstation", d.workstation, "hour_rate")
|
||||
if d.workstation:
|
||||
w = frappe.db.get_value("Workstation", d.workstation, ["hour_rate", "fixed_cycle_cost"])
|
||||
if not d.hour_rate:
|
||||
d.hour_rate = flt(w[0])
|
||||
|
||||
fixed_cost += flt(w[1])
|
||||
|
||||
if d.hour_rate and d.time_in_mins:
|
||||
d.operating_cost = flt(d.hour_rate) * flt(d.time_in_mins) / 60.0
|
||||
total_op_cost += flt(d.operating_cost)
|
||||
|
||||
self.operating_cost = total_op_cost
|
||||
|
||||
def calculate_fixed_cost(self):
|
||||
"""Update workstation rate and calculates totals"""
|
||||
fixed_cost = 0
|
||||
for d in self.get('bom_operations'):
|
||||
if d.workstation:
|
||||
fixed_cost += flt(frappe.db.get_value("Workstation", d.workstation, "fixed_cycle_cost"))
|
||||
self.total_fixed_cost = fixed_cost
|
||||
|
||||
|
||||
def calculate_rm_cost(self):
|
||||
"""Fetch RM rate as per today's valuation rate and calculate totals"""
|
||||
total_rm_cost = 0
|
||||
|
||||
@@ -96,7 +96,7 @@ cur_frm.cscript['Transfer Raw Materials'] = function() {
|
||||
}
|
||||
|
||||
cur_frm.cscript['Update Finished Goods'] = function() {
|
||||
cur_frm.cscript.make_se('Manufacture/Repack');
|
||||
cur_frm.cscript.make_se('Manufacture');
|
||||
}
|
||||
|
||||
cur_frm.fields_dict['production_item'].get_query = function(doc) {
|
||||
|
||||
@@ -109,15 +109,15 @@
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.docstatus==1",
|
||||
"description": "Automatically updated via Stock Entry of type Manufacture/Repack",
|
||||
"fieldname": "produced_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Manufactured Qty",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "produced_qty",
|
||||
"oldfieldtype": "Currency",
|
||||
"permlevel": 0,
|
||||
"depends_on": "eval:doc.docstatus==1",
|
||||
"description": "Automatically updated via Stock Entry of type Manufacture or Repack",
|
||||
"fieldname": "produced_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Manufactured Qty",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "produced_qty",
|
||||
"oldfieldtype": "Currency",
|
||||
"permlevel": 0,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
|
||||
@@ -103,7 +103,7 @@ class ProductionOrder(Document):
|
||||
status = "Submitted"
|
||||
if stock_entries:
|
||||
status = "In Process"
|
||||
produced_qty = stock_entries.get("Manufacture/Repack")
|
||||
produced_qty = stock_entries.get("Manufacture")
|
||||
if flt(produced_qty) == flt(self.qty):
|
||||
status = "Completed"
|
||||
|
||||
@@ -113,7 +113,7 @@ class ProductionOrder(Document):
|
||||
def update_produced_qty(self):
|
||||
produced_qty = frappe.db.sql("""select sum(fg_completed_qty)
|
||||
from `tabStock Entry` where production_order=%s and docstatus=1
|
||||
and purpose='Manufacture/Repack'""", self.name)
|
||||
and purpose='Manufacture'""", self.name)
|
||||
produced_qty = flt(produced_qty[0][0]) if produced_qty else 0
|
||||
|
||||
if produced_qty > self.qty:
|
||||
|
||||
@@ -31,7 +31,7 @@ class TestProductionOrder(unittest.TestCase):
|
||||
s.submit()
|
||||
|
||||
# from wip to fg
|
||||
s = frappe.get_doc(make_stock_entry(pro_doc.name, "Manufacture/Repack", 4))
|
||||
s = frappe.get_doc(make_stock_entry(pro_doc.name, "Manufacture", 4))
|
||||
s.insert()
|
||||
s.submit()
|
||||
|
||||
@@ -49,7 +49,7 @@ class TestProductionOrder(unittest.TestCase):
|
||||
test_stock_entry.make_stock_entry("_Test Item", None, "_Test Warehouse - _TC", 100, 100)
|
||||
test_stock_entry.make_stock_entry("_Test Item Home Desktop 100", None, "_Test Warehouse - _TC", 100, 100)
|
||||
|
||||
s = frappe.get_doc(make_stock_entry(pro_doc.name, "Manufacture/Repack", 7))
|
||||
s = frappe.get_doc(make_stock_entry(pro_doc.name, "Manufacture", 7))
|
||||
s.insert()
|
||||
|
||||
self.assertRaises(StockOverProductionError, s.submit)
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
"doctype": "Report",
|
||||
"idx": 1,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2014-06-03 07:18:17.082436",
|
||||
"modified": "2014-09-17 12:41:55.740299",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Issued Items Against Production Order",
|
||||
"owner": "Administrator",
|
||||
"query": "select\n ste.production_order as \"Production Order:Link/Production Order:120\",\n ste.posting_date as \"Issue Date:Date:140\",\n ste_item.item_code as \"Item Code:Link/Item:120\",\n\tste_item.description as \"Description::150\",\n\tste_item.transfer_qty as \"Qty:Float:100\",\n\tste_item.stock_uom as \"UOM:Link/UOM:80\",\n\tste_item.amount as \"Amount:Currency:120\",\n\tste_item.serial_no as \"Serial No:Link/Serial No:80\",\n\tste_item.s_warehouse as \"Source Warehouse:Link/Warehouse:120\",\n\tste_item.t_warehouse as \"Target Warehouse:Link/Warehouse:120\",\n\tpro.production_item as \"Finished Goods:Link/Item:120\", \n\tste.name as \"Stock Entry:Link/Stock Entry:120\"\nfrom\n\t`tabStock Entry` ste, `tabStock Entry Detail` ste_item, `tabProduction Order` pro\nwhere\n\tifnull(ste.production_order, '') != '' and ste.name = ste_item.parent \n\tand ste.production_order = pro.name and ste.docstatus = 1 \n\tand ste.purpose = 'Manufacture/Repack'\norder by ste.posting_date, ste.production_order, ste_item.item_code",
|
||||
"query": "select\n ste.production_order as \"Production Order:Link/Production Order:120\",\n ste.posting_date as \"Issue Date:Date:140\",\n ste_item.item_code as \"Item Code:Link/Item:120\",\n\tste_item.description as \"Description::150\",\n\tste_item.transfer_qty as \"Qty:Float:100\",\n\tste_item.stock_uom as \"UOM:Link/UOM:80\",\n\tste_item.amount as \"Amount:Currency:120\",\n\tste_item.serial_no as \"Serial No:Link/Serial No:80\",\n\tste_item.s_warehouse as \"Source Warehouse:Link/Warehouse:120\",\n\tste_item.t_warehouse as \"Target Warehouse:Link/Warehouse:120\",\n\tpro.production_item as \"Finished Goods:Link/Item:120\", \n\tste.name as \"Stock Entry:Link/Stock Entry:120\"\nfrom\n\t`tabStock Entry` ste, `tabStock Entry Detail` ste_item, `tabProduction Order` pro\nwhere\n\tifnull(ste.production_order, '') != '' and ste.name = ste_item.parent \n\tand ste.production_order = pro.name and ste.docstatus = 1 \n\tand ste.purpose = 'Manufacture' or 'Repack'\norder by ste.posting_date, ste.production_order, ste_item.item_code",
|
||||
"ref_doctype": "Production Order",
|
||||
"report_name": "Issued Items Against Production Order",
|
||||
"report_type": "Query Report"
|
||||
|
||||
@@ -80,4 +80,10 @@ execute:frappe.delete_doc("DocType", "Landed Cost Wizard")
|
||||
erpnext.patches.v4_2.default_website_style
|
||||
erpnext.patches.v4_2.set_company_country
|
||||
erpnext.patches.v4_2.update_sales_order_invoice_field_name
|
||||
erpnext.patches.v4_2.cost_of_production_cycle
|
||||
erpnext.patches.v4_2.cost_of_production_cycle
|
||||
erpnext.patches.v4_2.seprate_manufacture_and_repack
|
||||
execute:frappe.delete_doc("Report", "Warehouse-Wise Stock Balance")
|
||||
execute:frappe.delete_doc("DocType", "Purchase Request")
|
||||
execute:frappe.delete_doc("DocType", "Purchase Request Item")
|
||||
erpnext.patches.v4_2.recalculate_bom_cost
|
||||
erpnext.patches.v4_2.fix_gl_entries_for_stock_transactions
|
||||
|
||||
@@ -2,6 +2,7 @@ import frappe
|
||||
from frappe.templates.pages.style_settings import default_properties
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc('website', 'doctype', 'style_settings')
|
||||
style_settings = frappe.get_doc("Style Settings", "Style Settings")
|
||||
if not style_settings.apply_style:
|
||||
style_settings.update(default_properties)
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.utils import flt
|
||||
|
||||
def execute():
|
||||
from erpnext.utilities.repost_stock import repost
|
||||
repost(allow_zero_rate=True)
|
||||
|
||||
warehouse_account = frappe.db.sql("""select name, master_name from tabAccount
|
||||
where ifnull(account_type, '') = 'Warehouse'""")
|
||||
warehouses = [d[1] for d in warehouse_account]
|
||||
accounts = [d[0] for d in warehouse_account]
|
||||
|
||||
stock_vouchers = frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
|
||||
from `tabStock Ledger Entry` sle
|
||||
where sle.warehouse in (%s)
|
||||
order by sle.posting_date""" %
|
||||
', '.join(['%s']*len(warehouses)), tuple(warehouses))
|
||||
|
||||
rejected = []
|
||||
for voucher_type, voucher_no in stock_vouchers:
|
||||
stock_bal = frappe.db.sql("""select sum(stock_value_difference) from `tabStock Ledger Entry`
|
||||
where voucher_type=%s and voucher_no =%s and warehouse in (%s)""" %
|
||||
('%s', '%s', ', '.join(['%s']*len(warehouses))), tuple([voucher_type, voucher_no] + warehouses))
|
||||
|
||||
account_bal = frappe.db.sql("""select ifnull(sum(ifnull(debit, 0) - ifnull(credit, 0)), 0)
|
||||
from `tabGL Entry`
|
||||
where voucher_type=%s and voucher_no =%s and account in (%s)
|
||||
group by voucher_type, voucher_no""" %
|
||||
('%s', '%s', ', '.join(['%s']*len(accounts))), tuple([voucher_type, voucher_no] + accounts))
|
||||
|
||||
if stock_bal and account_bal and abs(flt(stock_bal[0][0]) - flt(account_bal[0][0])) > 0.1:
|
||||
try:
|
||||
print voucher_type, voucher_no, stock_bal[0][0], account_bal[0][0]
|
||||
|
||||
frappe.db.sql("""delete from `tabGL Entry`
|
||||
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
|
||||
|
||||
voucher = frappe.get_doc(voucher_type, voucher_no)
|
||||
voucher.make_gl_entries(repost_future_gle=False)
|
||||
frappe.db.commit()
|
||||
except Exception, e:
|
||||
print frappe.get_traceback()
|
||||
rejected.append([voucher_type, voucher_no])
|
||||
frappe.db.rollback()
|
||||
|
||||
print "Failed to repost: "
|
||||
print rejected
|
||||
16
erpnext/patches/v4_2/recalculate_bom_cost.py
Normal file
16
erpnext/patches/v4_2/recalculate_bom_cost.py
Normal file
@@ -0,0 +1,16 @@
|
||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
for d in frappe.db.sql("select name from `tabBOM` where docstatus < 2"):
|
||||
try:
|
||||
document = frappe.get_doc('BOM', d[0])
|
||||
if document.docstatus == 1:
|
||||
document.ignore_validate_update_after_submit = True
|
||||
document.calculate_cost()
|
||||
document.save()
|
||||
except:
|
||||
pass
|
||||
9
erpnext/patches/v4_2/seprate_manufacture_and_repack.py
Normal file
9
erpnext/patches/v4_2/seprate_manufacture_and_repack.py
Normal file
@@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
frappe.db.sql("""update `tabStock Entry` set purpose='Manufacture' where purpose='Manufacture/Repack' and ifnull(production_order,"")!="" """)
|
||||
frappe.db.sql("""update `tabStock Entry` set purpose='Repack' where purpose='Manufacture/Repack' and ifnull(production_order,"")="" """)
|
||||
@@ -6,17 +6,17 @@ frappe.views.calendar["Task"] = {
|
||||
"start": "exp_start_date",
|
||||
"end": "exp_end_date",
|
||||
"id": "name",
|
||||
"title": __("subject"),
|
||||
"title": "subject",
|
||||
"allDay": "allDay"
|
||||
},
|
||||
gantt: true,
|
||||
filters: [
|
||||
{
|
||||
"fieldtype": "Link",
|
||||
"fieldname": "project",
|
||||
"options": "Project",
|
||||
"fieldtype": "Link",
|
||||
"fieldname": "project",
|
||||
"options": "Project",
|
||||
"label": __("Project")
|
||||
}
|
||||
],
|
||||
get_events_method: "erpnext.projects.doctype.task.task.get_events"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ var get_filters = function(){
|
||||
{ "value": "Item Group", "label": __("Item Group") },
|
||||
{ "value": "Supplier", "label": __("Supplier") },
|
||||
{ "value": "Supplier Type", "label": __("Supplier Type") },
|
||||
{ "value": "Supplier Type", "label": __("Project") }
|
||||
{ "value": "Project", "label": __("Project") }
|
||||
],
|
||||
"default": "Item"
|
||||
},
|
||||
|
||||
@@ -25,7 +25,7 @@ var get_filters = function(){
|
||||
{ "value": "Customer", "label": __("Customer") },
|
||||
{ "value": "Customer Group", "label": __("Customer Group") },
|
||||
{ "value": "Territory", "label": __("Territory") },
|
||||
{ "value": "Supplier Type", "label": __("Project") }
|
||||
{ "value": "Project", "label": __("Project") }
|
||||
],
|
||||
"default": "Item"
|
||||
},
|
||||
|
||||
@@ -138,9 +138,20 @@ erpnext.StockAnalytics = erpnext.StockGridReport.extend({
|
||||
item.valuation_method : sys_defaults.valuation_method;
|
||||
var is_fifo = valuation_method == "FIFO";
|
||||
|
||||
var diff = me.get_value_diff(wh, sl, is_fifo);
|
||||
if(sl.voucher_type=="Stock Reconciliation") {
|
||||
var diff = (sl.qty_after_transaction * sl.valuation_rate) - item.closing_qty_value;
|
||||
wh.fifo_stack.push([sl.qty_after_transaction, sl.valuation_rate, sl.posting_date]);
|
||||
wh.balance_qty = sl.qty_after_transaction;
|
||||
wh.balance_value = sl.valuation_rate * sl.qty_after_transaction;
|
||||
} else {
|
||||
var diff = me.get_value_diff(wh, sl, is_fifo);
|
||||
}
|
||||
} else {
|
||||
var diff = sl.qty;
|
||||
if(sl.voucher_type=="Stock Reconciliation") {
|
||||
var diff = sl.qty_after_transaction - item.closing_qty_value;
|
||||
} else {
|
||||
var diff = sl.qty;
|
||||
}
|
||||
}
|
||||
|
||||
if(posting_datetime < from_date) {
|
||||
@@ -150,6 +161,8 @@ erpnext.StockAnalytics = erpnext.StockGridReport.extend({
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
item.closing_qty_value += diff;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -9,8 +9,8 @@ erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
|
||||
};
|
||||
return this.item_warehouse[item][warehouse];
|
||||
},
|
||||
|
||||
get_value_diff: function(wh, sl, is_fifo) {
|
||||
|
||||
get_value_diff: function(wh, sl, is_fifo) {
|
||||
// value
|
||||
if(sl.qty > 0) {
|
||||
// incoming - rate is given
|
||||
@@ -30,9 +30,9 @@ erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
|
||||
} else {
|
||||
var value_diff = (rate * add_qty);
|
||||
}
|
||||
|
||||
|
||||
if(add_qty)
|
||||
wh.fifo_stack.push([add_qty, sl.incoming_rate, sl.posting_date]);
|
||||
wh.fifo_stack.push([add_qty, sl.incoming_rate, sl.posting_date]);
|
||||
} else {
|
||||
// called everytime for maintaining fifo stack
|
||||
var fifo_value_diff = this.get_fifo_value_diff(wh, sl);
|
||||
@@ -44,13 +44,13 @@ erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
|
||||
var value_diff = fifo_value_diff;
|
||||
} else {
|
||||
// average rate for weighted average
|
||||
var rate = (wh.balance_qty.toFixed(2) == 0.00 ? 0 :
|
||||
var rate = (wh.balance_qty.toFixed(2) == 0.00 ? 0 :
|
||||
flt(wh.balance_value) / flt(wh.balance_qty));
|
||||
|
||||
|
||||
// no change in value if negative qty
|
||||
if((wh.balance_qty + sl.qty).toFixed(2) >= 0.00)
|
||||
var value_diff = (rate * sl.qty);
|
||||
else
|
||||
else
|
||||
var value_diff = -wh.balance_value;
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,6 @@ erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
|
||||
// update balance (only needed in case of valuation)
|
||||
wh.balance_qty += sl.qty;
|
||||
wh.balance_value += value_diff;
|
||||
|
||||
return value_diff;
|
||||
},
|
||||
get_fifo_value_diff: function(wh, sl) {
|
||||
@@ -66,19 +65,19 @@ erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
|
||||
var fifo_stack = (wh.fifo_stack || []).reverse();
|
||||
var fifo_value_diff = 0.0;
|
||||
var qty = -sl.qty;
|
||||
|
||||
|
||||
for(var i=0, j=fifo_stack.length; i<j; i++) {
|
||||
var batch = fifo_stack.pop();
|
||||
if(batch[0] >= qty) {
|
||||
batch[0] = batch[0] - qty;
|
||||
fifo_value_diff += (qty * batch[1]);
|
||||
|
||||
|
||||
qty = 0.0;
|
||||
if(batch[0]) {
|
||||
// batch still has qty put it back
|
||||
fifo_stack.push(batch);
|
||||
}
|
||||
|
||||
|
||||
// all qty found
|
||||
break;
|
||||
} else {
|
||||
@@ -87,35 +86,34 @@ erpnext.StockGridReport = frappe.views.TreeGridReport.extend({
|
||||
qty = qty - batch[0];
|
||||
}
|
||||
}
|
||||
|
||||
// reset the updated stack
|
||||
wh.fifo_stack = fifo_stack.reverse();
|
||||
return -fifo_value_diff;
|
||||
},
|
||||
|
||||
|
||||
get_serialized_value_diff: function(sl) {
|
||||
var me = this;
|
||||
|
||||
|
||||
var value_diff = 0.0;
|
||||
|
||||
|
||||
$.each(sl.serial_no.trim().split("\n"), function(i, sr) {
|
||||
if(sr) {
|
||||
value_diff += flt(me.serialized_buying_rates[sr.trim().toLowerCase()]);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return value_diff;
|
||||
},
|
||||
|
||||
|
||||
get_serialized_buying_rates: function() {
|
||||
var serialized_buying_rates = {};
|
||||
|
||||
|
||||
if (frappe.report_dump.data["Serial No"]) {
|
||||
$.each(frappe.report_dump.data["Serial No"], function(i, sn) {
|
||||
serialized_buying_rates[sn.name.toLowerCase()] = flt(sn.incoming_rate);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return serialized_buying_rates;
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -155,7 +155,7 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
|
||||
project_name: item.project_name || me.frm.doc.project_name
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
me.frm.script_manager.trigger("price_list_rate", cdt, cdn);
|
||||
@@ -827,4 +827,35 @@ erpnext.TransactionController = erpnext.stock.StockController.extend({
|
||||
.appendTo($(this.frm.fields_dict.other_charges_calculation.wrapper).empty());
|
||||
}
|
||||
},
|
||||
|
||||
is_recurring: function() {
|
||||
// set default values for recurring documents
|
||||
if(this.frm.doc.is_recurring) {
|
||||
var owner_email = this.frm.doc.owner=="Administrator"
|
||||
? frappe.user_info("Administrator").email
|
||||
: this.frm.doc.owner;
|
||||
|
||||
this.frm.doc.notification_email_address = $.map([cstr(owner_email),
|
||||
cstr(this.frm.doc.contact_email)], function(v) { return v || null; }).join(", ");
|
||||
this.frm.doc.repeat_on_day_of_month = frappe.datetime.str_to_obj(this.frm.doc.posting_date).getDate();
|
||||
}
|
||||
|
||||
refresh_many(["notification_email_address", "repeat_on_day_of_month"]);
|
||||
},
|
||||
|
||||
from_date: function() {
|
||||
// set to_date
|
||||
if(this.frm.doc.from_date) {
|
||||
var recurring_type_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6,
|
||||
'Yearly': 12};
|
||||
|
||||
var months = recurring_type_map[this.frm.doc.recurring_type];
|
||||
if(months) {
|
||||
var to_date = frappe.datetime.add_months(this.frm.doc.from_date,
|
||||
months);
|
||||
this.frm.doc.to_date = frappe.datetime.add_days(to_date, -1);
|
||||
refresh_field('to_date');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -195,37 +195,6 @@ cur_frm.cscript.on_submit = function(doc, cdt, cdn) {
|
||||
}
|
||||
};
|
||||
|
||||
cur_frm.cscript.is_recurring = function(doc, dt, dn) {
|
||||
// set default values for recurring orders
|
||||
if(doc.is_recurring) {
|
||||
var owner_email = doc.owner=="Administrator"
|
||||
? frappe.user_info("Administrator").email
|
||||
: doc.owner;
|
||||
|
||||
doc.notification_email_address = $.map([cstr(owner_email),
|
||||
cstr(doc.contact_email)], function(v) { return v || null; }).join(", ");
|
||||
doc.repeat_on_day_of_month = frappe.datetime.str_to_obj(doc.posting_date).getDate();
|
||||
}
|
||||
|
||||
refresh_many(["notification_email_address", "repeat_on_day_of_month"]);
|
||||
}
|
||||
|
||||
cur_frm.cscript.from_date = function(doc, dt, dn) {
|
||||
// set to_date
|
||||
if(doc.from_date) {
|
||||
var recurring_type_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6,
|
||||
'Yearly': 12};
|
||||
|
||||
var months = recurring_type_map[doc.recurring_type];
|
||||
if(months) {
|
||||
var to_date = frappe.datetime.add_months(doc.from_date,
|
||||
months);
|
||||
doc.to_date = frappe.datetime.add_days(to_date, -1);
|
||||
refresh_field('to_date');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript.send_sms = function() {
|
||||
frappe.require("assets/erpnext/js/sms_manager.js");
|
||||
var sms_man = new SMSManager(cur_frm.doc);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,14 +4,10 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import frappe.utils
|
||||
|
||||
from frappe.utils import cstr, flt, getdate, comma_and
|
||||
|
||||
from frappe import _
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
|
||||
from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document
|
||||
|
||||
from erpnext.controllers.selling_controller import SellingController
|
||||
|
||||
form_grid_templates = {
|
||||
@@ -122,8 +118,6 @@ class SalesOrder(SellingController):
|
||||
if not self.billing_status: self.billing_status = 'Not Billed'
|
||||
if not self.delivery_status: self.delivery_status = 'Not Delivered'
|
||||
|
||||
validate_recurring_document(self)
|
||||
|
||||
def validate_warehouse(self):
|
||||
from erpnext.stock.utils import validate_warehouse_company
|
||||
|
||||
@@ -157,6 +151,8 @@ class SalesOrder(SellingController):
|
||||
doc.set_status(update=True)
|
||||
|
||||
def on_submit(self):
|
||||
super(SalesOrder, self).on_submit()
|
||||
|
||||
self.update_stock_ledger(update_stock = 1)
|
||||
|
||||
self.check_credit(self.grand_total)
|
||||
@@ -165,8 +161,6 @@ class SalesOrder(SellingController):
|
||||
|
||||
self.update_prevdoc_status('submit')
|
||||
frappe.db.set(self, 'status', 'Submitted')
|
||||
|
||||
convert_to_recurring(self, "SO/REC/.#####", self.transaction_date)
|
||||
|
||||
def on_cancel(self):
|
||||
# Cannot cancel stopped SO
|
||||
@@ -255,11 +249,6 @@ class SalesOrder(SellingController):
|
||||
def get_portal_page(self):
|
||||
return "order" if self.docstatus==1 else None
|
||||
|
||||
def on_update_after_submit(self):
|
||||
validate_recurring_document(self)
|
||||
convert_to_recurring(self, "SO/REC/.#####", self.transaction_date)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_material_request(source_name, target_doc=None):
|
||||
def postprocess(source, doc):
|
||||
|
||||
@@ -3,19 +3,11 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
from frappe.utils import cstr, flt, has_common, make_esc, comma_or
|
||||
|
||||
from frappe.utils import cstr, flt, has_common, comma_or
|
||||
from frappe import session, _
|
||||
|
||||
|
||||
from erpnext.utilities.transaction_base import TransactionBase
|
||||
|
||||
class AuthorizationControl(TransactionBase):
|
||||
|
||||
|
||||
# Get Names of all Approving Users and Roles
|
||||
# -------------------------------------------
|
||||
def get_appr_user_role(self, det, doctype_name, total, based_on, condition, item, company):
|
||||
amt_list, appr_users, appr_roles = [], [], []
|
||||
users, roles = '',''
|
||||
@@ -24,10 +16,18 @@ class AuthorizationControl(TransactionBase):
|
||||
amt_list.append(flt(x[0]))
|
||||
max_amount = max(amt_list)
|
||||
|
||||
app_dtl = frappe.db.sql("select approving_user, approving_role from `tabAuthorization Rule` where transaction = %s and (value = %s or value > %s) and docstatus != 2 and based_on = %s and company = %s %s" % ('%s', '%s', '%s', '%s', '%s', condition), (doctype_name, flt(max_amount), total, based_on, company))
|
||||
app_dtl = frappe.db.sql("""select approving_user, approving_role from `tabAuthorization Rule`
|
||||
where transaction = %s and (value = %s or value > %s)
|
||||
and docstatus != 2 and based_on = %s and company = %s %s""" %
|
||||
('%s', '%s', '%s', '%s', '%s', condition),
|
||||
(doctype_name, flt(max_amount), total, based_on, company))
|
||||
|
||||
if not app_dtl:
|
||||
app_dtl = frappe.db.sql("select approving_user, approving_role from `tabAuthorization Rule` where transaction = %s and (value = %s or value > %s) and docstatus != 2 and based_on = %s and ifnull(company,'') = '' %s" % ('%s', '%s', '%s', '%s', condition), (doctype_name, flt(max_amount), total, based_on))
|
||||
app_dtl = frappe.db.sql("""select approving_user, approving_role from `tabAuthorization Rule`
|
||||
where transaction = %s and (value = %s or value > %s) and docstatus != 2
|
||||
and based_on = %s and ifnull(company,'') = '' %s""" %
|
||||
('%s', '%s', '%s', '%s', condition), (doctype_name, flt(max_amount), total, based_on))
|
||||
|
||||
for d in app_dtl:
|
||||
if(d[0]): appr_users.append(d[0])
|
||||
if(d[1]): appr_roles.append(d[1])
|
||||
@@ -36,43 +36,56 @@ class AuthorizationControl(TransactionBase):
|
||||
frappe.msgprint(_("Not authroized since {0} exceeds limits").format(_(based_on)))
|
||||
frappe.throw(_("Can be approved by {0}").format(comma_or(appr_roles + appr_users)))
|
||||
|
||||
|
||||
# Check if authorization rule is set specific to user
|
||||
# ----------------------------------------------------
|
||||
def validate_auth_rule(self, doctype_name, total, based_on, cond, company, item = ''):
|
||||
chk = 1
|
||||
add_cond1,add_cond2 = '',''
|
||||
if based_on == 'Itemwise Discount':
|
||||
add_cond1 += " and master_name = '"+cstr(item)+"'"
|
||||
itemwise_exists = frappe.db.sql("select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and company = %s and docstatus != 2 %s %s" % ('%s', '%s', '%s', '%s', cond, add_cond1), (doctype_name, total, based_on, company))
|
||||
add_cond1 += " and master_name = '"+cstr(item).replace("'", "\\'")+"'"
|
||||
itemwise_exists = frappe.db.sql("""select value from `tabAuthorization Rule`
|
||||
where transaction = %s and value <= %s
|
||||
and based_on = %s and company = %s and docstatus != 2 %s %s""" %
|
||||
('%s', '%s', '%s', '%s', cond, add_cond1), (doctype_name, total, based_on, company))
|
||||
|
||||
if not itemwise_exists:
|
||||
itemwise_exists = frappe.db.sql("select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and ifnull(company,'') = '' and docstatus != 2 %s %s" % ('%s', '%s', '%s', cond, add_cond1), (doctype_name, total, based_on))
|
||||
itemwise_exists = frappe.db.sql("""select value from `tabAuthorization Rule`
|
||||
where transaction = %s and value <= %s and based_on = %s
|
||||
and ifnull(company,'') = '' and docstatus != 2 %s %s""" %
|
||||
('%s', '%s', '%s', cond, add_cond1), (doctype_name, total, based_on))
|
||||
|
||||
if itemwise_exists:
|
||||
self.get_appr_user_role(itemwise_exists, doctype_name, total, based_on, cond+add_cond1, item,company)
|
||||
chk = 0
|
||||
if chk == 1:
|
||||
if based_on == 'Itemwise Discount': add_cond2 += " and ifnull(master_name,'') = ''"
|
||||
appr = frappe.db.sql("select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and company = %s and docstatus != 2 %s %s" % ('%s', '%s', '%s', '%s', cond, add_cond2), (doctype_name, total, based_on, company))
|
||||
if based_on == 'Itemwise Discount':
|
||||
add_cond2 += " and ifnull(master_name,'') = ''"
|
||||
|
||||
appr = frappe.db.sql("""select value from `tabAuthorization Rule`
|
||||
where transaction = %s and value <= %s and based_on = %s
|
||||
and company = %s and docstatus != 2 %s %s""" %
|
||||
('%s', '%s', '%s', '%s', cond, add_cond2), (doctype_name, total, based_on, company))
|
||||
|
||||
if not appr:
|
||||
appr = frappe.db.sql("select value from `tabAuthorization Rule` where transaction = %s and value <= %s and based_on = %s and ifnull(company,'') = '' and docstatus != 2 %s %s"% ('%s', '%s', '%s', cond, add_cond2), (doctype_name, total, based_on))
|
||||
appr = frappe.db.sql("""select value from `tabAuthorization Rule`
|
||||
where transaction = %s and value <= %s and based_on = %s
|
||||
and ifnull(company,'') = '' and docstatus != 2 %s %s""" %
|
||||
('%s', '%s', '%s', cond, add_cond2), (doctype_name, total, based_on))
|
||||
|
||||
self.get_appr_user_role(appr, doctype_name, total, based_on, cond+add_cond2, item, company)
|
||||
|
||||
|
||||
# Bifurcate Authorization based on type
|
||||
# --------------------------------------
|
||||
def bifurcate_based_on_type(self, doctype_name, total, av_dis, based_on, doc_obj, val, company):
|
||||
add_cond = ''
|
||||
auth_value = av_dis
|
||||
if val == 1: add_cond += " and system_user = '"+session['user']+"'"
|
||||
|
||||
if val == 1: add_cond += " and system_user = '"+session['user'].replace("'", "\\'")+"'"
|
||||
elif val == 2: add_cond += " and system_role IN %s" % ("('"+"','".join(frappe.user.get_roles())+"')")
|
||||
else: add_cond += " and ifnull(system_user,'') = '' and ifnull(system_role,'') = ''"
|
||||
|
||||
if based_on == 'Grand Total': auth_value = total
|
||||
elif based_on == 'Customerwise Discount':
|
||||
if doc_obj:
|
||||
if doc_obj.doctype == 'Sales Invoice': customer = doc_obj.customer
|
||||
else: customer = doc_obj.customer_name
|
||||
add_cond = " and master_name = '"+make_esc("'")(cstr(customer))+"'"
|
||||
add_cond = " and master_name = '"+cstr(customer).replace("'", "\\'")+"'"
|
||||
if based_on == 'Itemwise Discount':
|
||||
if doc_obj:
|
||||
for t in doc_obj.get(doc_obj.fname):
|
||||
@@ -80,9 +93,6 @@ class AuthorizationControl(TransactionBase):
|
||||
else:
|
||||
self.validate_auth_rule(doctype_name, auth_value, based_on, add_cond, company)
|
||||
|
||||
|
||||
# Check Approving Authority for transactions other than expense voucher and Appraisal
|
||||
# -------------------------
|
||||
def validate_approving_authority(self, doctype_name,company, total, doc_obj = ''):
|
||||
av_dis = 0
|
||||
if doc_obj:
|
||||
@@ -94,11 +104,12 @@ class AuthorizationControl(TransactionBase):
|
||||
if price_list_rate: av_dis = 100 - flt(base_rate * 100 / price_list_rate)
|
||||
|
||||
final_based_on = ['Grand Total','Average Discount','Customerwise Discount','Itemwise Discount']
|
||||
# Individual User
|
||||
# ================
|
||||
# Check for authorization set for individual user
|
||||
|
||||
based_on = [x[0] for x in frappe.db.sql("select distinct based_on from `tabAuthorization Rule` where transaction = %s and system_user = %s and (company = %s or ifnull(company,'')='') and docstatus != 2", (doctype_name, session['user'], company))]
|
||||
# Check for authorization set for individual user
|
||||
based_on = [x[0] for x in frappe.db.sql("""select distinct based_on from `tabAuthorization Rule`
|
||||
where transaction = %s and system_user = %s
|
||||
and (company = %s or ifnull(company,'')='') and docstatus != 2""",
|
||||
(doctype_name, session['user'], company))]
|
||||
|
||||
for d in based_on:
|
||||
self.bifurcate_based_on_type(doctype_name, total, av_dis, d, doc_obj, 1, company)
|
||||
@@ -107,8 +118,6 @@ class AuthorizationControl(TransactionBase):
|
||||
for r in based_on:
|
||||
if r in final_based_on and r != 'Itemwise Discount': final_based_on.remove(r)
|
||||
|
||||
# Specific Role
|
||||
# ===============
|
||||
# Check for authorization set on particular roles
|
||||
based_on = [x[0] for x in frappe.db.sql("""select based_on
|
||||
from `tabAuthorization Rule`
|
||||
@@ -124,19 +133,24 @@ class AuthorizationControl(TransactionBase):
|
||||
for r in based_on:
|
||||
if r in final_based_on and r != 'Itemwise Discount': final_based_on.remove(r)
|
||||
|
||||
# Global Rule
|
||||
# =============
|
||||
# Check for global authorization
|
||||
for g in final_based_on:
|
||||
self.bifurcate_based_on_type(doctype_name, total, av_dis, g, doc_obj, 0, company)
|
||||
|
||||
#========================================================================================================================
|
||||
# payroll related check
|
||||
def get_value_based_rule(self,doctype_name,employee,total_claimed_amount,company):
|
||||
val_lst =[]
|
||||
val = frappe.db.sql("select value from `tabAuthorization Rule` where transaction=%s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(value,0)< %s and company = %s and docstatus!=2",(doctype_name,employee,employee,total_claimed_amount,company))
|
||||
val = frappe.db.sql("""select value from `tabAuthorization Rule`
|
||||
where transaction=%s and (to_emp=%s or
|
||||
to_designation IN (select designation from `tabEmployee` where name=%s))
|
||||
and ifnull(value,0)< %s and company = %s and docstatus!=2""",
|
||||
(doctype_name,employee,employee,total_claimed_amount,company))
|
||||
|
||||
if not val:
|
||||
val = frappe.db.sql("select value from `tabAuthorization Rule` where transaction=%s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(value,0)< %s and ifnull(company,'') = '' and docstatus!=2",(doctype_name, employee, employee, total_claimed_amount))
|
||||
val = frappe.db.sql("""select value from `tabAuthorization Rule`
|
||||
where transaction=%s and (to_emp=%s or
|
||||
to_designation IN (select designation from `tabEmployee` where name=%s))
|
||||
and ifnull(value,0)< %s and ifnull(company,'') = '' and docstatus!=2""",
|
||||
(doctype_name, employee, employee, total_claimed_amount))
|
||||
|
||||
if val:
|
||||
val_lst = [y[0] for y in val]
|
||||
@@ -144,13 +158,23 @@ class AuthorizationControl(TransactionBase):
|
||||
val_lst.append(0)
|
||||
|
||||
max_val = max(val_lst)
|
||||
rule = frappe.db.sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and company = %s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(value,0)= %s and docstatus!=2",(doctype_name,company,employee,employee,flt(max_val)), as_dict=1)
|
||||
rule = frappe.db.sql("""select name, to_emp, to_designation, approving_role, approving_user
|
||||
from `tabAuthorization Rule`
|
||||
where transaction=%s and company = %s
|
||||
and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s))
|
||||
and ifnull(value,0)= %s and docstatus!=2""",
|
||||
(doctype_name,company,employee,employee,flt(max_val)), as_dict=1)
|
||||
|
||||
if not rule:
|
||||
rule = frappe.db.sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and ifnull(company,'') = '' and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(value,0)= %s and docstatus!=2",(doctype_name,employee,employee,flt(max_val)), as_dict=1)
|
||||
rule = frappe.db.sql("""select name, to_emp, to_designation, approving_role, approving_user
|
||||
from `tabAuthorization Rule`
|
||||
where transaction=%s and ifnull(company,'') = ''
|
||||
and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s))
|
||||
and ifnull(value,0)= %s and docstatus!=2""",
|
||||
(doctype_name,employee,employee,flt(max_val)), as_dict=1)
|
||||
|
||||
return rule
|
||||
|
||||
#---------------------------------------------------------------------------------------------------------------------
|
||||
# related to payroll module only
|
||||
def get_approver_name(self, doctype_name, total, doc_obj=''):
|
||||
app_user=[]
|
||||
@@ -159,11 +183,22 @@ class AuthorizationControl(TransactionBase):
|
||||
|
||||
if doc_obj:
|
||||
if doctype_name == 'Expense Claim':
|
||||
rule = self.get_value_based_rule(doctype_name,doc_obj.employee,doc_obj.total_claimed_amount, doc_obj.company)
|
||||
rule = self.get_value_based_rule(doctype_name, doc_obj.employee,
|
||||
doc_obj.total_claimed_amount, doc_obj.company)
|
||||
elif doctype_name == 'Appraisal':
|
||||
rule = frappe.db.sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and company = %s and docstatus!=2",(doctype_name,doc_obj.employee, doc_obj.employee, doc_obj.company),as_dict=1)
|
||||
rule = frappe.db.sql("""select name, to_emp, to_designation, approving_role, approving_user
|
||||
from `tabAuthorization Rule` where transaction=%s
|
||||
and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s))
|
||||
and company = %s and docstatus!=2""",
|
||||
(doctype_name,doc_obj.employee, doc_obj.employee, doc_obj.company),as_dict=1)
|
||||
|
||||
if not rule:
|
||||
rule = frappe.db.sql("select name, to_emp, to_designation, approving_role, approving_user from `tabAuthorization Rule` where transaction=%s and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s)) and ifnull(company,'') = '' and docstatus!=2",(doctype_name,doc_obj.employee, doc_obj.employee),as_dict=1)
|
||||
rule = frappe.db.sql("""select name, to_emp, to_designation, approving_role, approving_user
|
||||
from `tabAuthorization Rule`
|
||||
where transaction=%s and (to_emp=%s or
|
||||
to_designation IN (select designation from `tabEmployee` where name=%s))
|
||||
and ifnull(company,'') = '' and docstatus!=2""",
|
||||
(doctype_name,doc_obj.employee, doc_obj.employee), as_dict=1)
|
||||
|
||||
if rule:
|
||||
for m in rule:
|
||||
@@ -171,7 +206,11 @@ class AuthorizationControl(TransactionBase):
|
||||
if m['approving_user']:
|
||||
app_specific_user.append(m['approving_user'])
|
||||
elif m['approving_role']:
|
||||
user_lst = [z[0] for z in frappe.db.sql("select distinct t1.name from `tabUser` t1, `tabUserRole` t2 where t2.role=%s and t2.parent=t1.name and t1.name !='Administrator' and t1.name != 'Guest' and t1.docstatus !=2",m['approving_role'])]
|
||||
user_lst = [z[0] for z in frappe.db.sql("""select distinct t1.name
|
||||
from `tabUser` t1, `tabUserRole` t2 where t2.role=%s
|
||||
and t2.parent=t1.name and t1.name !='Administrator'
|
||||
and t1.name != 'Guest' and t1.docstatus !=2""", m['approving_role'])]
|
||||
|
||||
for x in user_lst:
|
||||
if not x in app_user:
|
||||
app_user.append(x)
|
||||
|
||||
@@ -48,11 +48,10 @@ class NamingSeries(Document):
|
||||
# validate names
|
||||
for i in options: self.validate_series_name(i)
|
||||
|
||||
if self.user_must_always_select:
|
||||
if options and self.user_must_always_select:
|
||||
options = [''] + options
|
||||
default = ''
|
||||
else:
|
||||
default = options[0]
|
||||
|
||||
default = options[0] if options else ''
|
||||
|
||||
# update in property setter
|
||||
prop_dict = {'options': "\n".join(options), 'default': default}
|
||||
|
||||
@@ -71,16 +71,17 @@ def setup_account(args=None):
|
||||
|
||||
frappe.db.set_default('desktop:home_page', 'desktop')
|
||||
|
||||
website_maker(args.company_name, args.company_tagline, args.name)
|
||||
website_maker(args.company_name.strip(), args.company_tagline, args.name)
|
||||
create_logo(args)
|
||||
|
||||
frappe.clear_cache()
|
||||
frappe.db.commit()
|
||||
|
||||
except:
|
||||
traceback = frappe.get_traceback()
|
||||
for hook in frappe.get_hooks("setup_wizard_exception"):
|
||||
frappe.get_attr(hook)(traceback, args)
|
||||
if args:
|
||||
traceback = frappe.get_traceback()
|
||||
for hook in frappe.get_hooks("setup_wizard_exception"):
|
||||
frappe.get_attr(hook)(traceback, args)
|
||||
|
||||
raise
|
||||
|
||||
@@ -134,7 +135,7 @@ def create_fiscal_year_and_company(args):
|
||||
frappe.get_doc({
|
||||
"doctype":"Company",
|
||||
'domain': args.get("industry"),
|
||||
'company_name':args.get('company_name'),
|
||||
'company_name':args.get('company_name').strip(),
|
||||
'abbr':args.get('company_abbr'),
|
||||
'default_currency':args.get('currency'),
|
||||
'country': args.get('country'),
|
||||
@@ -165,7 +166,7 @@ def set_defaults(args):
|
||||
global_defaults.update({
|
||||
'current_fiscal_year': args.curr_fiscal_year,
|
||||
'default_currency': args.get('currency'),
|
||||
'default_company':args.get('company_name'),
|
||||
'default_company':args.get('company_name').strip(),
|
||||
"country": args.get("country"),
|
||||
})
|
||||
|
||||
@@ -284,7 +285,7 @@ def create_taxes(args):
|
||||
try:
|
||||
frappe.get_doc({
|
||||
"doctype":"Account",
|
||||
"company": args.get("company_name"),
|
||||
"company": args.get("company_name").strip(),
|
||||
"parent_account": _("Duties and Taxes") + " - " + args.get("company_abbr"),
|
||||
"account_name": args.get("tax_" + str(i)),
|
||||
"group_or_ledger": "Ledger",
|
||||
@@ -344,7 +345,7 @@ def create_customers(args):
|
||||
"customer_type": "Company",
|
||||
"customer_group": _("Commercial"),
|
||||
"territory": args.get("country"),
|
||||
"company": args.get("company_name")
|
||||
"company": args.get("company_name").strip()
|
||||
}).insert()
|
||||
|
||||
if args.get("customer_contact_" + str(i)):
|
||||
@@ -364,7 +365,7 @@ def create_suppliers(args):
|
||||
"doctype":"Supplier",
|
||||
"supplier_name": supplier,
|
||||
"supplier_type": _("Local"),
|
||||
"company": args.get("company_name")
|
||||
"company": args.get("company_name").strip()
|
||||
}).insert()
|
||||
|
||||
if args.get("supplier_contact_" + str(i)):
|
||||
|
||||
@@ -78,7 +78,8 @@ data_map = {
|
||||
"Stock Ledger Entry": {
|
||||
"columns": ["name", "posting_date", "posting_time", "item_code", "warehouse",
|
||||
"actual_qty as qty", "voucher_type", "voucher_no", "project",
|
||||
"ifnull(incoming_rate,0) as incoming_rate", "stock_uom", "serial_no"],
|
||||
"ifnull(incoming_rate,0) as incoming_rate", "stock_uom", "serial_no",
|
||||
"qty_after_transaction", "valuation_rate"],
|
||||
"order_by": "posting_date, posting_time, name",
|
||||
"links": {
|
||||
"item_code": ["Item", "name"],
|
||||
|
||||
@@ -3,8 +3,15 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
|
||||
class Batch(Document):
|
||||
pass
|
||||
|
||||
def validate(self):
|
||||
self.item_has_batch_enabled()
|
||||
|
||||
def item_has_batch_enabled(self):
|
||||
has_batch_no = frappe.db.get_value("Item",self.item,"has_batch_no")
|
||||
if has_batch_no =='No':
|
||||
frappe.throw(_("The selected item cannot have Batch"))
|
||||
14
erpnext/stock/doctype/batch/test_batch.py
Normal file
14
erpnext/stock/doctype/batch/test_batch.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
import frappe
|
||||
from frappe.exceptions import ValidationError
|
||||
import unittest
|
||||
|
||||
class TestBatch(unittest.TestCase):
|
||||
def test_item_has_batch_enabled(self):
|
||||
self.assertRaises(ValidationError, frappe.get_doc({
|
||||
"doctype": "Batch",
|
||||
"name": "_test Batch",
|
||||
"item": "_Test Item"
|
||||
}).save)
|
||||
@@ -11,27 +11,27 @@ class Bin(Document):
|
||||
def validate(self):
|
||||
if self.get("__islocal") or not self.stock_uom:
|
||||
self.stock_uom = frappe.db.get_value('Item', self.item_code, 'stock_uom')
|
||||
|
||||
|
||||
self.validate_mandatory()
|
||||
|
||||
|
||||
self.projected_qty = flt(self.actual_qty) + flt(self.ordered_qty) + \
|
||||
flt(self.indented_qty) + flt(self.planned_qty) - flt(self.reserved_qty)
|
||||
|
||||
|
||||
def validate_mandatory(self):
|
||||
qf = ['actual_qty', 'reserved_qty', 'ordered_qty', 'indented_qty']
|
||||
for f in qf:
|
||||
if (not getattr(self, f, None)) or (not self.get(f)):
|
||||
if (not getattr(self, f, None)) or (not self.get(f)):
|
||||
self.set(f, 0.0)
|
||||
|
||||
|
||||
def update_stock(self, args):
|
||||
self.update_qty(args)
|
||||
|
||||
if args.get("actual_qty"):
|
||||
|
||||
if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation":
|
||||
from erpnext.stock.stock_ledger import update_entries_after
|
||||
|
||||
|
||||
if not args.get("posting_date"):
|
||||
args["posting_date"] = nowdate()
|
||||
|
||||
|
||||
# update valuation and qty after transaction for post dated entry
|
||||
update_entries_after({
|
||||
"item_code": self.item_code,
|
||||
@@ -39,21 +39,34 @@ class Bin(Document):
|
||||
"posting_date": args.get("posting_date"),
|
||||
"posting_time": args.get("posting_time")
|
||||
})
|
||||
|
||||
|
||||
def update_qty(self, args):
|
||||
# update the stock values (for current quantities)
|
||||
|
||||
self.actual_qty = flt(self.actual_qty) + flt(args.get("actual_qty"))
|
||||
if args.get("voucher_type")=="Stock Reconciliation":
|
||||
if args.get('is_cancelled') == 'No':
|
||||
self.actual_qty = args.get("qty_after_transaction")
|
||||
else:
|
||||
qty_after_transaction = frappe.db.get_value("""select qty_after_transaction
|
||||
from `tabStock Ledger Entry`
|
||||
where item_code=%s and warehouse=%s
|
||||
and not (voucher_type='Stock Reconciliation' and voucher_no=%s)
|
||||
order by posting_date desc limit 1""",
|
||||
(self.item_code, self.warehouse, args.get('voucher_no')))
|
||||
|
||||
self.actual_qty = flt(qty_after_transaction[0][0]) if qty_after_transaction else 0.0
|
||||
else:
|
||||
self.actual_qty = flt(self.actual_qty) + flt(args.get("actual_qty"))
|
||||
|
||||
self.ordered_qty = flt(self.ordered_qty) + flt(args.get("ordered_qty"))
|
||||
self.reserved_qty = flt(self.reserved_qty) + flt(args.get("reserved_qty"))
|
||||
self.indented_qty = flt(self.indented_qty) + flt(args.get("indented_qty"))
|
||||
self.planned_qty = flt(self.planned_qty) + flt(args.get("planned_qty"))
|
||||
|
||||
|
||||
self.projected_qty = flt(self.actual_qty) + flt(self.ordered_qty) + \
|
||||
flt(self.indented_qty) + flt(self.planned_qty) - flt(self.reserved_qty)
|
||||
|
||||
|
||||
self.save()
|
||||
|
||||
|
||||
def get_first_sle(self):
|
||||
sle = frappe.db.sql("""
|
||||
select * from `tabStock Ledger Entry`
|
||||
@@ -62,4 +75,4 @@ class Bin(Document):
|
||||
order by timestamp(posting_date, posting_time) asc, name asc
|
||||
limit 1
|
||||
""", (self.item_code, self.warehouse), as_dict=1)
|
||||
return sle and sle[0] or None
|
||||
return sle and sle[0] or None
|
||||
|
||||
@@ -19,7 +19,7 @@ cur_frm.cscript.refresh = function(doc) {
|
||||
cur_frm.cscript.edit_prices_button();
|
||||
|
||||
if (!doc.__islocal && doc.is_stock_item == 'Yes') {
|
||||
cur_frm.toggle_enable(['has_serial_no', 'is_stock_item', 'valuation_method'],
|
||||
cur_frm.toggle_enable(['has_serial_no', 'is_stock_item', 'valuation_method', 'has_batch_no'],
|
||||
(doc.__onload && doc.__onload.sle_exists=="exists") ? false : true);
|
||||
}
|
||||
|
||||
@@ -185,4 +185,4 @@ cur_frm.cscript.image = function() {
|
||||
else {
|
||||
msgprint(__("You may need to update: {0}", [frappe.meta.get_docfield(cur_frm.doc.doctype, "description_html").label]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -187,13 +187,14 @@ class Item(WebsiteGenerator):
|
||||
def cant_change(self):
|
||||
if not self.get("__islocal"):
|
||||
vals = frappe.db.get_value("Item", self.name,
|
||||
["has_serial_no", "is_stock_item", "valuation_method"], as_dict=True)
|
||||
["has_serial_no", "is_stock_item", "valuation_method", "has_batch_no"], as_dict=True)
|
||||
|
||||
if vals and ((self.is_stock_item == "No" and vals.is_stock_item == "Yes") or
|
||||
vals.has_serial_no != self.has_serial_no or
|
||||
vals.has_batch_no != self.has_batch_no or
|
||||
cstr(vals.valuation_method) != cstr(self.valuation_method)):
|
||||
if self.check_if_sle_exists() == "exists":
|
||||
frappe.throw(_("As there are existing stock transactions for this item, you can not change the values of 'Has Serial No', 'Is Stock Item' and 'Valuation Method'"))
|
||||
frappe.throw(_("As there are existing stock transactions for this item, you can not change the values of 'Has Serial No', 'Has Batch No', 'Is Stock Item' and 'Valuation Method'"))
|
||||
|
||||
def validate_item_type_for_reorder(self):
|
||||
if self.re_order_level or len(self.get("item_reorder", {"material_request_type": "Purchase"})):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
frappe.listview_settings['Item'] = {
|
||||
add_fields: ["item_name", "stock_uom", "item_group", "image",
|
||||
"is_stock_item", "is_sales_item", "is_purchase_item",
|
||||
"is_manufactured_item", "show_in_website"]
|
||||
add_fields: ["`tabItem`.`item_name`", "`tabItem`.`stock_uom`", "`tabItem`.`item_group`", "`tabItem`.`image`",
|
||||
"`tabItem`.`is_stock_item`", "`tabItem`.`is_sales_item`", "`tabItem`.`is_purchase_item`",
|
||||
"`tabItem`.`is_manufactured_item`", "`tabItem`.`show_in_website`"]
|
||||
};
|
||||
|
||||
@@ -9,6 +9,6 @@ class TestItem(unittest.TestCase):
|
||||
def test_duplicate_item(self):
|
||||
from erpnext.stock.doctype.item_price.item_price import ItemPriceDuplicateItem
|
||||
doc = frappe.copy_doc(test_records[0])
|
||||
self.assertRaises(ItemPriceDuplicateItem, doc.insert)
|
||||
self.assertRaises(ItemPriceDuplicateItem, doc.save)
|
||||
|
||||
test_records = frappe.get_test_records('Item Price')
|
||||
@@ -16,5 +16,11 @@
|
||||
"item_code": "_Test Item 2",
|
||||
"price_list": "_Test Price List Rest of the World",
|
||||
"price_list_rate": 20
|
||||
},
|
||||
{
|
||||
"doctype": "Item Price",
|
||||
"item_code": "_Test Item Home Desktop 100",
|
||||
"price_list": "_Test Price List",
|
||||
"price_list_rate": 1000
|
||||
}
|
||||
]
|
||||
|
||||
@@ -15,7 +15,7 @@ class LandedCostVoucher(Document):
|
||||
self.set("landed_cost_items", [])
|
||||
for pr in self.get("landed_cost_purchase_receipts"):
|
||||
pr_items = frappe.db.sql("""select pr_item.item_code, pr_item.description,
|
||||
pr_item.qty, pr_item.rate, pr_item.amount, pr_item.name
|
||||
pr_item.qty, pr_item.base_rate, pr_item.base_amount, pr_item.name
|
||||
from `tabPurchase Receipt Item` pr_item where parent = %s
|
||||
and exists(select name from tabItem where name = pr_item.item_code and is_stock_item = 'Yes')""",
|
||||
pr.purchase_receipt, as_dict=True)
|
||||
@@ -25,8 +25,8 @@ class LandedCostVoucher(Document):
|
||||
item.item_code = d.item_code
|
||||
item.description = d.description
|
||||
item.qty = d.qty
|
||||
item.rate = d.rate
|
||||
item.amount = d.amount
|
||||
item.rate = d.base_rate
|
||||
item.amount = d.base_amount
|
||||
item.purchase_receipt = pr.purchase_receipt
|
||||
item.purchase_receipt_item = d.name
|
||||
|
||||
@@ -97,10 +97,10 @@ class LandedCostVoucher(Document):
|
||||
|
||||
# update stock & gl entries for cancelled state of PR
|
||||
pr.docstatus = 2
|
||||
pr.update_stock()
|
||||
pr.update_stock_ledger()
|
||||
pr.make_gl_entries_on_cancel()
|
||||
|
||||
# update stock & gl entries for submit state of PR
|
||||
pr.docstatus = 1
|
||||
pr.update_stock()
|
||||
pr.update_stock_ledger()
|
||||
pr.make_gl_entries()
|
||||
|
||||
@@ -162,8 +162,7 @@ def item_details(doctype, txt, searchfield, start, page_len, filters):
|
||||
from erpnext.controllers.queries import get_match_cond
|
||||
return frappe.db.sql("""select name, item_name, description from `tabItem`
|
||||
where name in ( select item_code FROM `tabDelivery Note Item`
|
||||
where parent= %s
|
||||
and ifnull(qty, 0) > ifnull(packed_qty, 0))
|
||||
where parent= %s)
|
||||
and %s like "%s" %s
|
||||
limit %s, %s """ % ("%s", searchfield, "%s",
|
||||
get_match_cond(doctype), "%s", "%s"),
|
||||
|
||||
@@ -130,7 +130,7 @@ class PurchaseReceipt(BuyingController):
|
||||
if not d.prevdoc_docname:
|
||||
frappe.throw(_("Purchase Order number required for Item {0}").format(d.item_code))
|
||||
|
||||
def update_stock(self):
|
||||
def update_stock_ledger(self):
|
||||
sl_entries = []
|
||||
stock_items = self.get_stock_items()
|
||||
|
||||
@@ -234,7 +234,7 @@ class PurchaseReceipt(BuyingController):
|
||||
|
||||
self.update_ordered_qty()
|
||||
|
||||
self.update_stock()
|
||||
self.update_stock_ledger()
|
||||
|
||||
from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
|
||||
update_serial_nos_after_submit(self, "purchase_receipt_details")
|
||||
@@ -267,7 +267,7 @@ class PurchaseReceipt(BuyingController):
|
||||
|
||||
self.update_ordered_qty()
|
||||
|
||||
self.update_stock()
|
||||
self.update_stock_ledger()
|
||||
|
||||
self.update_prevdoc_status()
|
||||
pc_obj.update_last_purchase_rate(self, 0)
|
||||
|
||||
@@ -95,7 +95,7 @@ class TestPurchaseReceipt(unittest.TestCase):
|
||||
pr.insert()
|
||||
|
||||
self.assertEquals(len(pr.get("pr_raw_material_details")), 2)
|
||||
self.assertEquals(pr.get("purchase_receipt_details")[0].rm_supp_cost, 70000.0)
|
||||
self.assertEquals(pr.get("purchase_receipt_details")[0].rm_supp_cost, 20750.0)
|
||||
|
||||
|
||||
def test_serial_no_supplier(self):
|
||||
@@ -151,6 +151,6 @@ def set_perpetual_inventory(enable=1):
|
||||
accounts_settings.save()
|
||||
|
||||
|
||||
test_dependencies = ["BOM"]
|
||||
test_dependencies = ["BOM", "Item Price"]
|
||||
|
||||
test_records = frappe.get_test_records('Purchase Receipt')
|
||||
|
||||
@@ -120,7 +120,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
||||
clean_up: function() {
|
||||
// Clear Production Order record from locals, because it is updated via Stock Entry
|
||||
if(this.frm.doc.production_order &&
|
||||
this.frm.doc.purpose == "Manufacture/Repack") {
|
||||
this.frm.doc.purpose == "Manufacture") {
|
||||
frappe.model.remove_from_locals("Production Order",
|
||||
this.frm.doc.production_order);
|
||||
}
|
||||
@@ -162,7 +162,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
||||
},
|
||||
|
||||
toggle_enable_bom: function() {
|
||||
this.frm.toggle_enable("bom_no", !this.frm.doc.production_order);
|
||||
this.frm.toggle_enable("bom_no", this.frm.doc.purpose!="Manufacture");
|
||||
},
|
||||
|
||||
get_doctype_docname: function() {
|
||||
@@ -339,6 +339,8 @@ cur_frm.cscript.toggle_related_fields = function(doc) {
|
||||
cur_frm.fields_dict["mtn_details"].grid.set_column_disp("s_warehouse", !disable_from_warehouse);
|
||||
cur_frm.fields_dict["mtn_details"].grid.set_column_disp("t_warehouse", !disable_to_warehouse);
|
||||
|
||||
cur_frm.cscript.toggle_enable_bom();
|
||||
|
||||
if(doc.purpose == 'Purchase Return') {
|
||||
doc.customer = doc.customer_name = doc.customer_address =
|
||||
doc.delivery_note_no = doc.sales_invoice_no = null;
|
||||
@@ -351,6 +353,8 @@ cur_frm.cscript.toggle_related_fields = function(doc) {
|
||||
doc.delivery_note_no = doc.sales_invoice_no = doc.supplier =
|
||||
doc.supplier_name = doc.supplier_address = doc.purchase_receipt_no = null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
cur_frm.fields_dict['production_order'].get_query = function(doc) {
|
||||
@@ -457,4 +461,5 @@ cur_frm.fields_dict.customer.get_query = function(doc, cdt, cdn) {
|
||||
cur_frm.fields_dict.supplier.get_query = function(doc, cdt, cdn) {
|
||||
return { query: "erpnext.controllers.queries.supplier_query" }
|
||||
}
|
||||
cur_frm.add_fetch('production_order', 'total_fixed_cost', 'total_fixed_cost');
|
||||
cur_frm.add_fetch('production_order', 'total_fixed_cost', 'total_fixed_cost');
|
||||
cur_frm.add_fetch('bom_no', 'total_fixed_cost', 'total_fixed_cost');
|
||||
File diff suppressed because it is too large
Load Diff
@@ -43,7 +43,7 @@ class StockEntry(StockController):
|
||||
self.validate_uom_is_integer("uom", "qty")
|
||||
self.validate_uom_is_integer("stock_uom", "transfer_qty")
|
||||
self.validate_warehouse(pro_obj)
|
||||
self.validate_production_order(pro_obj)
|
||||
self.validate_production_order()
|
||||
self.get_stock_and_rate()
|
||||
self.validate_incoming_rate()
|
||||
self.validate_bom()
|
||||
@@ -54,6 +54,7 @@ class StockEntry(StockController):
|
||||
self.validate_valuation_rate()
|
||||
self.set_total_amount()
|
||||
|
||||
|
||||
def on_submit(self):
|
||||
self.update_stock_ledger()
|
||||
|
||||
@@ -74,7 +75,7 @@ class StockEntry(StockController):
|
||||
|
||||
def validate_purpose(self):
|
||||
valid_purposes = ["Material Issue", "Material Receipt", "Material Transfer",
|
||||
"Manufacture/Repack", "Subcontract", "Sales Return", "Purchase Return"]
|
||||
"Manufacture", "Repack", "Subcontract", "Sales Return", "Purchase Return"]
|
||||
if self.purpose not in valid_purposes:
|
||||
frappe.throw(_("Purpose must be one of {0}").format(comma_or(valid_purposes)))
|
||||
|
||||
@@ -137,7 +138,7 @@ class StockEntry(StockController):
|
||||
if self.purpose in target_mandatory and not d.t_warehouse:
|
||||
frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx))
|
||||
|
||||
if self.purpose == "Manufacture/Repack":
|
||||
if self.purpose in ["Manufacture", "Repack"]:
|
||||
if validate_for_manufacture_repack:
|
||||
if d.bom_no:
|
||||
d.s_warehouse = None
|
||||
@@ -156,14 +157,11 @@ class StockEntry(StockController):
|
||||
if cstr(d.s_warehouse) == cstr(d.t_warehouse):
|
||||
frappe.throw(_("Source and target warehouse cannot be same for row {0}").format(d.idx))
|
||||
|
||||
def validate_production_order(self, pro_obj=None):
|
||||
if not pro_obj:
|
||||
if self.production_order:
|
||||
pro_obj = frappe.get_doc('Production Order', self.production_order)
|
||||
else:
|
||||
return
|
||||
|
||||
if self.purpose == "Manufacture/Repack":
|
||||
def validate_production_order(self):
|
||||
if self.purpose == "Manufacture":
|
||||
# check if production order is entered
|
||||
if not self.production_order:
|
||||
frappe.throw(_("Production order number is mandatory for stock entry purpose manufacture"))
|
||||
# check for double entry
|
||||
self.check_duplicate_entry_for_production_order()
|
||||
elif self.purpose != "Material Transfer":
|
||||
@@ -192,7 +190,7 @@ class StockEntry(StockController):
|
||||
+ self.production_order + ":" + ", ".join(other_ste), DuplicateEntryForProductionOrderError)
|
||||
|
||||
def validate_valuation_rate(self):
|
||||
if self.purpose == "Manufacture/Repack":
|
||||
if self.purpose in ["Manufacture", "Repack"]:
|
||||
valuation_at_source, valuation_at_target = 0, 0
|
||||
for d in self.get("mtn_details"):
|
||||
if d.s_warehouse and not d.t_warehouse:
|
||||
@@ -248,7 +246,7 @@ class StockEntry(StockController):
|
||||
raw_material_cost += flt(d.amount)
|
||||
|
||||
# set incoming rate for fg item
|
||||
if self.purpose == "Manufacture/Repack":
|
||||
if self.purpose in ["Manufacture", "Repack"]:
|
||||
number_of_fg_items = len([t.t_warehouse for t in self.get("mtn_details") if t.t_warehouse])
|
||||
for d in self.get("mtn_details"):
|
||||
if d.bom_no or (d.t_warehouse and number_of_fg_items == 1):
|
||||
@@ -391,7 +389,7 @@ class StockEntry(StockController):
|
||||
pro_doc = frappe.get_doc("Production Order", self.production_order)
|
||||
_validate_production_order(pro_doc)
|
||||
pro_doc.run_method("update_status")
|
||||
if self.purpose == "Manufacture/Repack":
|
||||
if self.purpose == "Manufacture":
|
||||
pro_doc.run_method("update_produced_qty")
|
||||
self.update_planned_qty(pro_doc)
|
||||
|
||||
@@ -463,20 +461,20 @@ class StockEntry(StockController):
|
||||
|
||||
def get_items(self):
|
||||
self.set('mtn_details', [])
|
||||
self.validate_production_order()
|
||||
|
||||
pro_obj = None
|
||||
if self.production_order:
|
||||
# common validations
|
||||
pro_obj = frappe.get_doc('Production Order', self.production_order)
|
||||
if pro_obj:
|
||||
self.validate_production_order(pro_obj)
|
||||
self.bom_no = pro_obj.bom_no
|
||||
else:
|
||||
# invalid production order
|
||||
self.production_order = None
|
||||
|
||||
if self.bom_no:
|
||||
if self.purpose in ["Material Issue", "Material Transfer", "Manufacture/Repack",
|
||||
if self.purpose in ["Material Issue", "Material Transfer", "Manufacture", "Repack",
|
||||
"Subcontract"]:
|
||||
if self.production_order and self.purpose == "Material Transfer":
|
||||
item_dict = self.get_pending_raw_materials(pro_obj)
|
||||
@@ -493,7 +491,7 @@ class StockEntry(StockController):
|
||||
self.add_to_stock_entry_detail(item_dict)
|
||||
|
||||
# add finished good item to Stock Entry Detail table -- along with bom_no
|
||||
if self.production_order and self.purpose == "Manufacture/Repack":
|
||||
if self.production_order and self.purpose == "Manufacture":
|
||||
item = frappe.db.get_value("Item", pro_obj.production_item, ["item_name",
|
||||
"description", "stock_uom", "expense_account", "buying_cost_center"], as_dict=1)
|
||||
self.add_to_stock_entry_detail({
|
||||
@@ -509,7 +507,7 @@ class StockEntry(StockController):
|
||||
}
|
||||
}, bom_no=pro_obj.bom_no)
|
||||
|
||||
elif self.purpose in ["Material Receipt", "Manufacture/Repack"]:
|
||||
elif self.purpose in ["Material Receipt", "Repack"]:
|
||||
if self.purpose=="Material Receipt":
|
||||
self.from_warehouse = ""
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
"Material Issue": "icon-arrow-right",
|
||||
"Material Receipt": "icon-arrow-left",
|
||||
"Material Transfer": "icon-resize-horizontal",
|
||||
"Manufacture/Repack": "icon-wrench",
|
||||
"Manufacture": "icon-wrench",
|
||||
"Repack": "icon-wrench",
|
||||
"Sales Return": "icon-warning-sign",
|
||||
"Purchase Return": "icon-warning-sign",
|
||||
"Subcontract": "icon-truck"
|
||||
|
||||
@@ -108,6 +108,6 @@
|
||||
],
|
||||
"posting_date": "2013-01-25",
|
||||
"posting_time": "17:14:24",
|
||||
"purpose": "Manufacture/Repack"
|
||||
"purpose": "Repack"
|
||||
}
|
||||
]
|
||||
@@ -9,14 +9,64 @@ from erpnext.stock.doctype.serial_no.serial_no import *
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
|
||||
from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import StockFreezeError
|
||||
|
||||
def get_sle(**args):
|
||||
condition, values = "", []
|
||||
for key, value in args.iteritems():
|
||||
condition += " and " if condition else " where "
|
||||
condition += "`{0}`=%s".format(key)
|
||||
values.append(value)
|
||||
|
||||
return frappe.db.sql("""select * from `tabStock Ledger Entry` %s
|
||||
order by timestamp(posting_date, posting_time) desc, name desc limit 1"""% condition,
|
||||
values, as_dict=1)
|
||||
|
||||
def make_zero(item_code, warehouse):
|
||||
sle = get_sle(item_code = item_code, warehouse = warehouse)
|
||||
qty = sle[0].qty_after_transaction if sle else 0
|
||||
if qty < 0:
|
||||
make_stock_entry(item_code, None, warehouse, abs(qty), incoming_rate=10)
|
||||
elif qty > 0:
|
||||
make_stock_entry(item_code, warehouse, None, qty, incoming_rate=10)
|
||||
|
||||
class TestStockEntry(unittest.TestCase):
|
||||
|
||||
def tearDown(self):
|
||||
frappe.set_user("Administrator")
|
||||
set_perpetual_inventory(0)
|
||||
if hasattr(self, "old_default_company"):
|
||||
frappe.db.set_default("company", self.old_default_company)
|
||||
|
||||
def test_fifo(self):
|
||||
frappe.db.set_default("allow_negative_stock", 1)
|
||||
item_code = "_Test Item 2"
|
||||
warehouse = "_Test Warehouse - _TC"
|
||||
make_zero(item_code, warehouse)
|
||||
|
||||
make_stock_entry(item_code, None, warehouse, 1, incoming_rate=10)
|
||||
sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
|
||||
|
||||
self.assertEqual([[1, 10]], eval(sle.stock_queue))
|
||||
|
||||
# negative qty
|
||||
make_zero(item_code, warehouse)
|
||||
make_stock_entry(item_code, warehouse, None, 1, incoming_rate=10)
|
||||
sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
|
||||
|
||||
self.assertEqual([[-1, 10]], eval(sle.stock_queue))
|
||||
|
||||
# further negative
|
||||
make_stock_entry(item_code, warehouse, None, 1)
|
||||
sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
|
||||
|
||||
self.assertEqual([[-2, 10]], eval(sle.stock_queue))
|
||||
|
||||
# move stock to positive
|
||||
make_stock_entry(item_code, None, warehouse, 3, incoming_rate=10)
|
||||
sle = get_sle(item_code = item_code, warehouse = warehouse)[0]
|
||||
|
||||
self.assertEqual([[1, 10]], eval(sle.stock_queue))
|
||||
|
||||
frappe.db.set_default("allow_negative_stock", 0)
|
||||
|
||||
def test_auto_material_request(self):
|
||||
frappe.db.sql("""delete from `tabMaterial Request Item`""")
|
||||
frappe.db.sql("""delete from `tabMaterial Request`""")
|
||||
@@ -821,19 +871,19 @@ class TestStockEntry(unittest.TestCase):
|
||||
se = frappe.copy_doc(test_records[0]).insert()
|
||||
self.assertRaises (StockFreezeError, se.submit)
|
||||
frappe.db.set_value("Stock Settings", None, "stock_frozen_upto_days", 0)
|
||||
|
||||
|
||||
def test_production_order(self):
|
||||
bom_no = frappe.db.get_value("BOM", {"item": "_Test FG Item 2",
|
||||
bom_no = frappe.db.get_value("BOM", {"item": "_Test FG Item 2",
|
||||
"is_default": 1, "docstatus": 1})
|
||||
|
||||
|
||||
production_order = frappe.new_doc("Production Order")
|
||||
production_order.update({
|
||||
"company": "_Test Company",
|
||||
"fg_warehouse": "_Test Warehouse 1 - _TC",
|
||||
"production_item": "_Test FG Item 2",
|
||||
"fg_warehouse": "_Test Warehouse 1 - _TC",
|
||||
"production_item": "_Test FG Item 2",
|
||||
"bom_no": bom_no,
|
||||
"qty": 1.0,
|
||||
"stock_uom": "Nos",
|
||||
"stock_uom": "Nos",
|
||||
"wip_warehouse": "_Test Warehouse - _TC"
|
||||
})
|
||||
production_order.insert()
|
||||
@@ -843,7 +893,7 @@ class TestStockEntry(unittest.TestCase):
|
||||
|
||||
stock_entry = frappe.new_doc("Stock Entry")
|
||||
stock_entry.update({
|
||||
"purpose": "Manufacture/Repack",
|
||||
"purpose": "Manufacture",
|
||||
"production_order": production_order.name,
|
||||
"bom_no": bom_no,
|
||||
"fg_completed_qty": "1",
|
||||
|
||||
@@ -44,11 +44,14 @@ class StockLedgerEntry(Document):
|
||||
formatdate(self.posting_date), self.posting_time))
|
||||
|
||||
def validate_mandatory(self):
|
||||
mandatory = ['warehouse','posting_date','voucher_type','voucher_no','actual_qty','company']
|
||||
mandatory = ['warehouse','posting_date','voucher_type','voucher_no','company']
|
||||
for k in mandatory:
|
||||
if not self.get(k):
|
||||
frappe.throw(_("{0} is required").format(self.meta.get_label(k)))
|
||||
|
||||
if self.voucher_type != "Stock Reconciliation" and not self.actual_qty:
|
||||
frappe.throw(_("Actual Qty is mandatory"))
|
||||
|
||||
def validate_item(self):
|
||||
item_det = frappe.db.sql("""select name, has_batch_no, docstatus, is_stock_item
|
||||
from tabItem where name=%s""", self.item_code, as_dict=True)[0]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"allow_copy": 1,
|
||||
"allow_copy": 1,
|
||||
"autoname": "SR/.######",
|
||||
"creation": "2013-03-28 10:35:31",
|
||||
"description": "This tool helps you to update or fix the quantity and valuation of stock in the system. It is typically used to synchronise the system values and what actually exists in your warehouses.",
|
||||
@@ -7,6 +7,7 @@
|
||||
"doctype": "DocType",
|
||||
"fields": [
|
||||
{
|
||||
"default": "Today",
|
||||
"fieldname": "posting_date",
|
||||
"fieldtype": "Date",
|
||||
"in_filter": 0,
|
||||
@@ -118,7 +119,7 @@
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"max_attachments": 1,
|
||||
"modified": "2014-05-26 03:05:54.024413",
|
||||
"modified": "2014-10-07 12:43:52.825575",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Reconciliation",
|
||||
|
||||
@@ -16,13 +16,11 @@ class StockReconciliation(StockController):
|
||||
self.head_row = ["Item Code", "Warehouse", "Quantity", "Valuation Rate"]
|
||||
|
||||
def validate(self):
|
||||
self.entries = []
|
||||
|
||||
self.validate_data()
|
||||
self.validate_expense_account()
|
||||
|
||||
def on_submit(self):
|
||||
self.insert_stock_ledger_entries()
|
||||
self.update_stock_ledger()
|
||||
self.make_gl_entries()
|
||||
|
||||
def on_cancel(self):
|
||||
@@ -126,10 +124,9 @@ class StockReconciliation(StockController):
|
||||
except Exception, e:
|
||||
self.validation_messages.append(_("Row # ") + ("%d: " % (row_num)) + cstr(e))
|
||||
|
||||
def insert_stock_ledger_entries(self):
|
||||
def update_stock_ledger(self):
|
||||
""" find difference between current and expected entries
|
||||
and create stock ledger entries based on the difference"""
|
||||
from erpnext.stock.utils import get_valuation_method
|
||||
from erpnext.stock.stock_ledger import get_previous_sle
|
||||
|
||||
row_template = ["item_code", "warehouse", "qty", "valuation_rate"]
|
||||
@@ -141,105 +138,27 @@ class StockReconciliation(StockController):
|
||||
for row_num, row in enumerate(data[data.index(self.head_row)+1:]):
|
||||
row = frappe._dict(zip(row_template, row))
|
||||
row["row_num"] = row_num
|
||||
previous_sle = get_previous_sle({
|
||||
"item_code": row.item_code,
|
||||
"warehouse": row.warehouse,
|
||||
"posting_date": self.posting_date,
|
||||
"posting_time": self.posting_time
|
||||
})
|
||||
|
||||
# check valuation rate mandatory
|
||||
if row.qty not in ["", None] and not row.valuation_rate and \
|
||||
flt(previous_sle.get("qty_after_transaction")) <= 0:
|
||||
frappe.throw(_("Valuation Rate required for Item {0}").format(row.item_code))
|
||||
if row.qty in ("", None) or row.valuation_rate in ("", None):
|
||||
previous_sle = get_previous_sle({
|
||||
"item_code": row.item_code,
|
||||
"warehouse": row.warehouse,
|
||||
"posting_date": self.posting_date,
|
||||
"posting_time": self.posting_time
|
||||
})
|
||||
|
||||
change_in_qty = row.qty not in ["", None] and \
|
||||
(flt(row.qty) - flt(previous_sle.get("qty_after_transaction")))
|
||||
if row.qty in ("", None):
|
||||
row.qty = previous_sle.get("qty_after_transaction")
|
||||
|
||||
change_in_rate = row.valuation_rate not in ["", None] and \
|
||||
(flt(row.valuation_rate) - flt(previous_sle.get("valuation_rate")))
|
||||
if row.valuation_rate in ("", None):
|
||||
row.valuation_rate = previous_sle.get("valuation_rate")
|
||||
|
||||
if get_valuation_method(row.item_code) == "Moving Average":
|
||||
self.sle_for_moving_avg(row, previous_sle, change_in_qty, change_in_rate)
|
||||
# if row.qty and not row.valuation_rate:
|
||||
# frappe.throw(_("Valuation Rate required for Item {0}").format(row.item_code))
|
||||
|
||||
else:
|
||||
self.sle_for_fifo(row, previous_sle, change_in_qty, change_in_rate)
|
||||
self.insert_entries(row)
|
||||
|
||||
def sle_for_moving_avg(self, row, previous_sle, change_in_qty, change_in_rate):
|
||||
"""Insert Stock Ledger Entries for Moving Average valuation"""
|
||||
def _get_incoming_rate(qty, valuation_rate, previous_qty, previous_valuation_rate):
|
||||
if previous_valuation_rate == 0:
|
||||
return flt(valuation_rate)
|
||||
else:
|
||||
if valuation_rate in ["", None]:
|
||||
valuation_rate = previous_valuation_rate
|
||||
return (qty * valuation_rate - previous_qty * previous_valuation_rate) \
|
||||
/ flt(qty - previous_qty)
|
||||
|
||||
if change_in_qty:
|
||||
# if change in qty, irrespective of change in rate
|
||||
incoming_rate = _get_incoming_rate(flt(row.qty), flt(row.valuation_rate),
|
||||
flt(previous_sle.get("qty_after_transaction")), flt(previous_sle.get("valuation_rate")))
|
||||
|
||||
row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Actual Entry"
|
||||
self.insert_entries({"actual_qty": change_in_qty, "incoming_rate": incoming_rate}, row)
|
||||
|
||||
elif change_in_rate and flt(previous_sle.get("qty_after_transaction")) > 0:
|
||||
# if no change in qty, but change in rate
|
||||
# and positive actual stock before this reconciliation
|
||||
incoming_rate = _get_incoming_rate(
|
||||
flt(previous_sle.get("qty_after_transaction"))+1, flt(row.valuation_rate),
|
||||
flt(previous_sle.get("qty_after_transaction")),
|
||||
flt(previous_sle.get("valuation_rate")))
|
||||
|
||||
# +1 entry
|
||||
row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Valuation Adjustment +1"
|
||||
self.insert_entries({"actual_qty": 1, "incoming_rate": incoming_rate}, row)
|
||||
|
||||
# -1 entry
|
||||
row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Valuation Adjustment -1"
|
||||
self.insert_entries({"actual_qty": -1}, row)
|
||||
|
||||
def sle_for_fifo(self, row, previous_sle, change_in_qty, change_in_rate):
|
||||
"""Insert Stock Ledger Entries for FIFO valuation"""
|
||||
previous_stock_queue = json.loads(previous_sle.get("stock_queue") or "[]")
|
||||
previous_stock_qty = sum((batch[0] for batch in previous_stock_queue))
|
||||
previous_stock_value = sum((batch[0] * batch[1] for batch in \
|
||||
previous_stock_queue))
|
||||
|
||||
def _insert_entries():
|
||||
if previous_stock_queue != [[row.qty, row.valuation_rate]]:
|
||||
# make entry as per attachment
|
||||
if flt(row.qty):
|
||||
row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Actual Entry"
|
||||
self.insert_entries({"actual_qty": row.qty,
|
||||
"incoming_rate": flt(row.valuation_rate)}, row)
|
||||
|
||||
# Make reverse entry
|
||||
if previous_stock_qty:
|
||||
row["voucher_detail_no"] = "Row: " + cstr(row.row_num) + "/Reverse Entry"
|
||||
self.insert_entries({"actual_qty": -1 * previous_stock_qty,
|
||||
"incoming_rate": previous_stock_qty < 0 and
|
||||
flt(row.valuation_rate) or 0}, row)
|
||||
|
||||
|
||||
if change_in_qty:
|
||||
if row.valuation_rate in ["", None]:
|
||||
# dont want change in valuation
|
||||
if previous_stock_qty > 0:
|
||||
# set valuation_rate as previous valuation_rate
|
||||
row.valuation_rate = previous_stock_value / flt(previous_stock_qty)
|
||||
|
||||
_insert_entries()
|
||||
|
||||
elif change_in_rate and previous_stock_qty > 0:
|
||||
# if no change in qty, but change in rate
|
||||
# and positive actual stock before this reconciliation
|
||||
|
||||
row.qty = previous_stock_qty
|
||||
_insert_entries()
|
||||
|
||||
def insert_entries(self, opts, row):
|
||||
def insert_entries(self, row):
|
||||
"""Insert Stock Ledger Entries"""
|
||||
args = frappe._dict({
|
||||
"doctype": "Stock Ledger Entry",
|
||||
@@ -251,16 +170,13 @@ class StockReconciliation(StockController):
|
||||
"voucher_no": self.name,
|
||||
"company": self.company,
|
||||
"stock_uom": frappe.db.get_value("Item", row.item_code, "stock_uom"),
|
||||
"voucher_detail_no": row.voucher_detail_no,
|
||||
"fiscal_year": self.fiscal_year,
|
||||
"is_cancelled": "No"
|
||||
"is_cancelled": "No",
|
||||
"qty_after_transaction": row.qty,
|
||||
"valuation_rate": row.valuation_rate
|
||||
})
|
||||
args.update(opts)
|
||||
self.make_sl_entries([args])
|
||||
|
||||
# append to entries
|
||||
self.entries.append(args)
|
||||
|
||||
def delete_and_repost_sle(self):
|
||||
""" Delete Stock Ledger Entries related to this voucher
|
||||
and repost future Stock Ledger Entries"""
|
||||
@@ -295,7 +211,7 @@ class StockReconciliation(StockController):
|
||||
|
||||
if not self.expense_account:
|
||||
msgprint(_("Please enter Expense Account"), raise_exception=1)
|
||||
elif not frappe.db.sql("""select * from `tabStock Ledger Entry`"""):
|
||||
elif not frappe.db.sql("""select name from `tabStock Ledger Entry` limit 1"""):
|
||||
if frappe.db.get_value("Account", self.expense_account, "report_type") == "Profit and Loss":
|
||||
frappe.throw(_("Difference Account must be a 'Liability' type account, since this Stock Reconciliation is an Opening Entry"))
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ class TestStockReconciliation(unittest.TestCase):
|
||||
[20, "", "2012-12-26", "12:05", 16000, 15, 18000],
|
||||
[10, 2000, "2012-12-26", "12:10", 20000, 5, 6000],
|
||||
[1, 1000, "2012-12-01", "00:00", 1000, 11, 13200],
|
||||
[0, "", "2012-12-26", "12:10", 0, -5, 0]
|
||||
[0, "", "2012-12-26", "12:10", 0, -5, -6000]
|
||||
]
|
||||
|
||||
for d in input_data:
|
||||
@@ -63,16 +63,16 @@ class TestStockReconciliation(unittest.TestCase):
|
||||
input_data = [
|
||||
[50, 1000, "2012-12-26", "12:00", 50000, 45, 48000],
|
||||
[5, 1000, "2012-12-26", "12:00", 5000, 0, 0],
|
||||
[15, 1000, "2012-12-26", "12:00", 15000, 10, 12000],
|
||||
[15, 1000, "2012-12-26", "12:00", 15000, 10, 11500],
|
||||
[25, 900, "2012-12-26", "12:00", 22500, 20, 22500],
|
||||
[20, 500, "2012-12-26", "12:00", 10000, 15, 18000],
|
||||
[50, 1000, "2013-01-01", "12:00", 50000, 65, 68000],
|
||||
[5, 1000, "2013-01-01", "12:00", 5000, 20, 23000],
|
||||
["", 1000, "2012-12-26", "12:05", 15000, 10, 12000],
|
||||
["", 1000, "2012-12-26", "12:05", 15000, 10, 11500],
|
||||
[20, "", "2012-12-26", "12:05", 18000, 15, 18000],
|
||||
[10, 2000, "2012-12-26", "12:10", 20000, 5, 6000],
|
||||
[1, 1000, "2012-12-01", "00:00", 1000, 11, 13200],
|
||||
[0, "", "2012-12-26", "12:10", 0, -5, 0]
|
||||
[10, 2000, "2012-12-26", "12:10", 20000, 5, 7600],
|
||||
[1, 1000, "2012-12-01", "00:00", 1000, 11, 12512.73],
|
||||
[0, "", "2012-12-26", "12:10", 0, -5, -5142.86]
|
||||
|
||||
]
|
||||
|
||||
|
||||
@@ -6,18 +6,17 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
from frappe.utils import cint
|
||||
from frappe.model.document import Document
|
||||
|
||||
class StockSettings(Document):
|
||||
|
||||
def validate(self):
|
||||
for key in ["item_naming_by", "item_group", "stock_uom",
|
||||
"allow_negative_stock"]:
|
||||
for key in ["item_naming_by", "item_group", "stock_uom", "allow_negative_stock"]:
|
||||
frappe.db.set_default(key, self.get(key, ""))
|
||||
|
||||
|
||||
from erpnext.setup.doctype.naming_series.naming_series import set_by_naming_series
|
||||
set_by_naming_series("Item", "item_code",
|
||||
set_by_naming_series("Item", "item_code",
|
||||
self.get("item_naming_by")=="Naming Series", hide_name_field=True)
|
||||
|
||||
stock_frozen_limit = 356
|
||||
@@ -25,3 +24,5 @@ class StockSettings(Document):
|
||||
if submitted_stock_frozen > stock_frozen_limit:
|
||||
self.stock_frozen_upto_days = stock_frozen_limit
|
||||
frappe.msgprint (_("`Freeze Stocks Older Than` should be smaller than %d days.") %stock_frozen_limit)
|
||||
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Stock balances on a particular day, per warehouse.
|
||||
@@ -1,181 +0,0 @@
|
||||
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
frappe.require("assets/erpnext/js/stock_analytics.js");
|
||||
|
||||
frappe.pages['stock-balance'].onload = function(wrapper) {
|
||||
frappe.ui.make_app_page({
|
||||
parent: wrapper,
|
||||
title: __('Stock Balance'),
|
||||
single_column: true
|
||||
});
|
||||
|
||||
new erpnext.StockBalance(wrapper);
|
||||
|
||||
wrapper.appframe.add_module_icon("Stock");
|
||||
}
|
||||
|
||||
erpnext.StockBalance = erpnext.StockAnalytics.extend({
|
||||
init: function(wrapper) {
|
||||
this._super(wrapper, {
|
||||
title: __("Stock Balance"),
|
||||
doctypes: ["Item", "Item Group", "Warehouse", "Stock Ledger Entry", "Brand",
|
||||
"Stock Entry", "Project", "Serial No"],
|
||||
});
|
||||
},
|
||||
setup_columns: function() {
|
||||
this.columns = [
|
||||
{id: "name", name: __("Item"), field: "name", width: 300,
|
||||
formatter: this.tree_formatter},
|
||||
{id: "item_name", name: __("Item Name"), field: "item_name", width: 100},
|
||||
{id: "description", name: __("Description"), field: "description", width: 200,
|
||||
formatter: this.text_formatter},
|
||||
{id: "brand", name: __("Brand"), field: "brand", width: 100},
|
||||
{id: "stock_uom", name: __("UOM"), field: "stock_uom", width: 100},
|
||||
{id: "opening_qty", name: __("Opening Qty"), field: "opening_qty", width: 100,
|
||||
formatter: this.currency_formatter},
|
||||
{id: "inflow_qty", name: __("In Qty"), field: "inflow_qty", width: 100,
|
||||
formatter: this.currency_formatter},
|
||||
{id: "outflow_qty", name: __("Out Qty"), field: "outflow_qty", width: 100,
|
||||
formatter: this.currency_formatter},
|
||||
{id: "closing_qty", name: __("Closing Qty"), field: "closing_qty", width: 100,
|
||||
formatter: this.currency_formatter},
|
||||
|
||||
{id: "opening_value", name: __("Opening Value"), field: "opening_value", width: 100,
|
||||
formatter: this.currency_formatter},
|
||||
{id: "inflow_value", name: __("In Value"), field: "inflow_value", width: 100,
|
||||
formatter: this.currency_formatter},
|
||||
{id: "outflow_value", name: __("Out Value"), field: "outflow_value", width: 100,
|
||||
formatter: this.currency_formatter},
|
||||
{id: "closing_value", name: __("Closing Value"), field: "closing_value", width: 100,
|
||||
formatter: this.currency_formatter},
|
||||
{id: "valuation_rate", name: __("Valuation Rate"), field: "valuation_rate", width: 100,
|
||||
formatter: this.currency_formatter},
|
||||
];
|
||||
},
|
||||
|
||||
filters: [
|
||||
{fieldtype:"Select", label: __("Brand"), link:"Brand", fieldname: "brand",
|
||||
default_value: __("Select Brand..."), filter: function(val, item, opts) {
|
||||
return val == opts.default_value || item.brand == val || item._show;
|
||||
}, link_formatter: {filter_input: "brand"}},
|
||||
{fieldtype:"Select", label: __("Warehouse"), link:"Warehouse", fieldname: "warehouse",
|
||||
default_value: __("Select Warehouse..."), filter: function(val, item, opts, me) {
|
||||
return me.apply_zero_filter(val, item, opts, me);
|
||||
}},
|
||||
{fieldtype:"Select", label: __("Project"), link:"Project", fieldname: "project",
|
||||
default_value: __("Select Project..."), filter: function(val, item, opts, me) {
|
||||
return me.apply_zero_filter(val, item, opts, me);
|
||||
}, link_formatter: {filter_input: "project"}},
|
||||
{fieldtype:"Date", label: __("From Date"), fieldname: "from_date"},
|
||||
{fieldtype:"Label", label: __("To")},
|
||||
{fieldtype:"Date", label: __("To Date"), fieldname: "to_date"},
|
||||
{fieldtype:"Button", label: __("Refresh"), icon:"icon-refresh icon-white"},
|
||||
{fieldtype:"Button", label: __("Reset Filters"), icon: "icon-filter"}
|
||||
],
|
||||
|
||||
setup_plot_check: function() {
|
||||
return;
|
||||
},
|
||||
|
||||
prepare_data: function() {
|
||||
this.stock_entry_map = this.make_name_map(frappe.report_dump.data["Stock Entry"], "name");
|
||||
this._super();
|
||||
},
|
||||
|
||||
prepare_balances: function() {
|
||||
var me = this;
|
||||
var from_date = dateutil.str_to_obj(this.from_date);
|
||||
var to_date = dateutil.str_to_obj(this.to_date);
|
||||
var data = frappe.report_dump.data["Stock Ledger Entry"];
|
||||
|
||||
this.item_warehouse = {};
|
||||
this.serialized_buying_rates = this.get_serialized_buying_rates();
|
||||
|
||||
for(var i=0, j=data.length; i<j; i++) {
|
||||
var sl = data[i];
|
||||
var sl_posting_date = dateutil.str_to_obj(sl.posting_date);
|
||||
|
||||
if((me.is_default("warehouse") ? true : me.warehouse == sl.warehouse) &&
|
||||
(me.is_default("project") ? true : me.project == sl.project)) {
|
||||
var item = me.item_by_name[sl.item_code];
|
||||
var wh = me.get_item_warehouse(sl.warehouse, sl.item_code);
|
||||
var valuation_method = item.valuation_method ?
|
||||
item.valuation_method : sys_defaults.valuation_method;
|
||||
var is_fifo = valuation_method == "FIFO";
|
||||
|
||||
var qty_diff = sl.qty;
|
||||
var value_diff = me.get_value_diff(wh, sl, is_fifo);
|
||||
|
||||
if(sl_posting_date < from_date) {
|
||||
item.opening_qty += qty_diff;
|
||||
item.opening_value += value_diff;
|
||||
} else if(sl_posting_date <= to_date) {
|
||||
var ignore_inflow_outflow = this.is_default("warehouse")
|
||||
&& sl.voucher_type=="Stock Entry"
|
||||
&& this.stock_entry_map[sl.voucher_no].purpose=="Material Transfer";
|
||||
|
||||
if(!ignore_inflow_outflow) {
|
||||
if(qty_diff < 0) {
|
||||
item.outflow_qty += Math.abs(qty_diff);
|
||||
} else {
|
||||
item.inflow_qty += qty_diff;
|
||||
}
|
||||
if(value_diff < 0) {
|
||||
item.outflow_value += Math.abs(value_diff);
|
||||
} else {
|
||||
item.inflow_value += value_diff;
|
||||
}
|
||||
|
||||
item.closing_qty += qty_diff;
|
||||
item.closing_value += value_diff;
|
||||
}
|
||||
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// opening + diff = closing
|
||||
// adding opening, since diff already added to closing
|
||||
$.each(me.item_by_name, function(key, item) {
|
||||
item.closing_qty += item.opening_qty;
|
||||
item.closing_value += item.opening_value;
|
||||
|
||||
// valuation rate
|
||||
if(!item.is_group && flt(item.closing_qty) > 0)
|
||||
item.valuation_rate = flt(item.closing_value) / flt(item.closing_qty);
|
||||
else item.valuation_rate = 0.0
|
||||
});
|
||||
},
|
||||
|
||||
update_groups: function() {
|
||||
var me = this;
|
||||
|
||||
$.each(this.data, function(i, item) {
|
||||
// update groups
|
||||
if(!item.is_group && me.apply_filter(item, "brand")) {
|
||||
var parent = me.parent_map[item.name];
|
||||
while(parent) {
|
||||
parent_group = me.item_by_name[parent];
|
||||
$.each(me.columns, function(c, col) {
|
||||
if (col.formatter == me.currency_formatter && col.field != "valuation_rate") {
|
||||
parent_group[col.field] = flt(parent_group[col.field]) + flt(item[col.field]);
|
||||
}
|
||||
});
|
||||
|
||||
// show parent if filtered by brand
|
||||
if(item.brand == me.brand)
|
||||
parent_group._show = true;
|
||||
|
||||
parent = me.parent_map[parent];
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
get_plot_data: function() {
|
||||
return;
|
||||
}
|
||||
});
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"creation": "2012-12-27 18:57:47.000000",
|
||||
"docstatus": 0,
|
||||
"doctype": "Page",
|
||||
"icon": "icon-table",
|
||||
"idx": 1,
|
||||
"modified": "2013-07-11 14:44:15.000000",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "stock-balance",
|
||||
"owner": "Administrator",
|
||||
"page_name": "stock-balance",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Material Manager"
|
||||
},
|
||||
{
|
||||
"role": "Analytics"
|
||||
}
|
||||
],
|
||||
"standard": "Yes",
|
||||
"title": "Stock Balance"
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import flt
|
||||
from frappe.utils import flt, cint
|
||||
|
||||
def execute(filters=None):
|
||||
if not filters: filters = {}
|
||||
@@ -57,6 +57,7 @@ def get_stock_ledger_entries(filters):
|
||||
conditions, as_dict=1)
|
||||
|
||||
def get_item_warehouse_batch_map(filters):
|
||||
float_precision = cint(frappe.db.get_default("float_precision")) or 3
|
||||
sle = get_stock_ledger_entries(filters)
|
||||
iwb_map = {}
|
||||
|
||||
@@ -67,14 +68,14 @@ def get_item_warehouse_batch_map(filters):
|
||||
}))
|
||||
qty_dict = iwb_map[d.item_code][d.warehouse][d.batch_no]
|
||||
if d.posting_date < filters["from_date"]:
|
||||
qty_dict.opening_qty += flt(d.actual_qty)
|
||||
qty_dict.opening_qty += flt(d.actual_qty, float_precision)
|
||||
elif d.posting_date >= filters["from_date"] and d.posting_date <= filters["to_date"]:
|
||||
if flt(d.actual_qty) > 0:
|
||||
qty_dict.in_qty += flt(d.actual_qty)
|
||||
qty_dict.in_qty += flt(d.actual_qty, float_precision)
|
||||
else:
|
||||
qty_dict.out_qty += abs(flt(d.actual_qty))
|
||||
qty_dict.out_qty += abs(flt(d.actual_qty, float_precision))
|
||||
|
||||
qty_dict.bal_qty += flt(d.actual_qty)
|
||||
qty_dict.bal_qty += flt(d.actual_qty, float_precision)
|
||||
|
||||
return iwb_map
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import date_diff
|
||||
from frappe.utils import date_diff, flt
|
||||
|
||||
def execute(filters=None):
|
||||
|
||||
|
||||
columns = get_columns()
|
||||
item_details = get_fifo_queue(filters)
|
||||
to_date = filters["to_date"]
|
||||
@@ -16,35 +16,40 @@ def execute(filters=None):
|
||||
fifo_queue = item_dict["fifo_queue"]
|
||||
details = item_dict["details"]
|
||||
if not fifo_queue: continue
|
||||
|
||||
|
||||
average_age = get_average_age(fifo_queue, to_date)
|
||||
earliest_age = date_diff(to_date, fifo_queue[0][1])
|
||||
latest_age = date_diff(to_date, fifo_queue[-1][1])
|
||||
|
||||
data.append([item, details.item_name, details.description, details.item_group,
|
||||
|
||||
data.append([item, details.item_name, details.description, details.item_group,
|
||||
details.brand, average_age, earliest_age, latest_age, details.stock_uom])
|
||||
|
||||
|
||||
return columns, data
|
||||
|
||||
|
||||
def get_average_age(fifo_queue, to_date):
|
||||
batch_age = age_qty = total_qty = 0.0
|
||||
for batch in fifo_queue:
|
||||
batch_age = date_diff(to_date, batch[1])
|
||||
age_qty += batch_age * batch[0]
|
||||
total_qty += batch[0]
|
||||
|
||||
|
||||
return (age_qty / total_qty) if total_qty else 0.0
|
||||
|
||||
|
||||
def get_columns():
|
||||
return [_("Item Code") + ":Link/Item:100", _("Item Name") + "::100", _("Description") + "::200",
|
||||
_("Item Group") + ":Link/Item Group:100", _("Brand") + ":Link/Brand:100", _("Average Age") + ":Float:100",
|
||||
return [_("Item Code") + ":Link/Item:100", _("Item Name") + "::100", _("Description") + "::200",
|
||||
_("Item Group") + ":Link/Item Group:100", _("Brand") + ":Link/Brand:100", _("Average Age") + ":Float:100",
|
||||
_("Earliest") + ":Int:80", _("Latest") + ":Int:80", _("UOM") + ":Link/UOM:100"]
|
||||
|
||||
|
||||
def get_fifo_queue(filters):
|
||||
item_details = {}
|
||||
prev_qty = 0.0
|
||||
for d in get_stock_ledger_entries(filters):
|
||||
item_details.setdefault(d.name, {"details": d, "fifo_queue": []})
|
||||
fifo_queue = item_details[d.name]["fifo_queue"]
|
||||
|
||||
if d.voucher_type == "Stock Reconciliation":
|
||||
d.actual_qty = flt(d.qty_after_transaction) - flt(prev_qty)
|
||||
|
||||
if d.actual_qty > 0:
|
||||
fifo_queue.append([d.actual_qty, d.posting_date])
|
||||
else:
|
||||
@@ -52,7 +57,7 @@ def get_fifo_queue(filters):
|
||||
while qty_to_pop:
|
||||
batch = fifo_queue[0] if fifo_queue else [0, None]
|
||||
if 0 < batch[0] <= qty_to_pop:
|
||||
# if batch qty > 0
|
||||
# if batch qty > 0
|
||||
# not enough or exactly same qty in current batch, clear batch
|
||||
qty_to_pop -= batch[0]
|
||||
fifo_queue.pop(0)
|
||||
@@ -61,12 +66,14 @@ def get_fifo_queue(filters):
|
||||
batch[0] -= qty_to_pop
|
||||
qty_to_pop = 0
|
||||
|
||||
prev_qty = d.qty_after_transaction
|
||||
|
||||
return item_details
|
||||
|
||||
|
||||
def get_stock_ledger_entries(filters):
|
||||
return frappe.db.sql("""select
|
||||
item.name, item.item_name, item_group, brand, description, item.stock_uom,
|
||||
actual_qty, posting_date
|
||||
return frappe.db.sql("""select
|
||||
item.name, item.item_name, item_group, brand, description, item.stock_uom,
|
||||
actual_qty, posting_date, voucher_type, qty_after_transaction
|
||||
from `tabStock Ledger Entry` sle,
|
||||
(select name, item_name, description, stock_uom, brand, item_group
|
||||
from `tabItem` {item_conditions}) item
|
||||
@@ -77,19 +84,19 @@ def get_stock_ledger_entries(filters):
|
||||
order by posting_date, posting_time, sle.name"""\
|
||||
.format(item_conditions=get_item_conditions(filters),
|
||||
sle_conditions=get_sle_conditions(filters)), filters, as_dict=True)
|
||||
|
||||
|
||||
def get_item_conditions(filters):
|
||||
conditions = []
|
||||
if filters.get("item_code"):
|
||||
conditions.append("item_code=%(item_code)s")
|
||||
if filters.get("brand"):
|
||||
conditions.append("brand=%(brand)s")
|
||||
|
||||
|
||||
return "where {}".format(" and ".join(conditions)) if conditions else ""
|
||||
|
||||
|
||||
def get_sle_conditions(filters):
|
||||
conditions = []
|
||||
if filters.get("warehouse"):
|
||||
conditions.append("warehouse=%(warehouse)s")
|
||||
|
||||
return "and {}".format(" and ".join(conditions)) if conditions else ""
|
||||
|
||||
return "and {}".format(" and ".join(conditions)) if conditions else ""
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.query_reports["Warehouse-Wise Stock Balance"] = {
|
||||
frappe.query_reports["Stock Balance"] = {
|
||||
"filters": [
|
||||
{
|
||||
"fieldname":"from_date",
|
||||
@@ -18,4 +18,4 @@ frappe.query_reports["Warehouse-Wise Stock Balance"] = {
|
||||
"default": frappe.datetime.get_today()
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,17 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2013-06-05 11:00:31",
|
||||
"creation": "2014-10-10 17:58:11.577901",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 1,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2014-06-03 07:18:17.384923",
|
||||
"modified": "2014-10-10 17:58:11.577901",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Warehouse-Wise Stock Balance",
|
||||
"name": "Stock Balance",
|
||||
"owner": "Administrator",
|
||||
"ref_doctype": "Stock Ledger Entry",
|
||||
"report_name": "Warehouse-Wise Stock Balance",
|
||||
"report_name": "Stock Balance",
|
||||
"report_type": "Script Report"
|
||||
}
|
||||
@@ -58,10 +58,10 @@ def get_conditions(filters):
|
||||
#get all details
|
||||
def get_stock_ledger_entries(filters):
|
||||
conditions = get_conditions(filters)
|
||||
return frappe.db.sql("""select item_code, warehouse, posting_date,
|
||||
actual_qty, valuation_rate, stock_uom, company
|
||||
return frappe.db.sql("""select item_code, warehouse, posting_date, actual_qty, valuation_rate,
|
||||
stock_uom, company, voucher_type, qty_after_transaction, stock_value_difference
|
||||
from `tabStock Ledger Entry`
|
||||
where docstatus < 2 %s order by item_code, warehouse""" %
|
||||
where docstatus < 2 %s order by posting_date, posting_time, name""" %
|
||||
conditions, as_dict=1)
|
||||
|
||||
def get_item_warehouse_map(filters):
|
||||
@@ -71,30 +71,36 @@ def get_item_warehouse_map(filters):
|
||||
for d in sle:
|
||||
iwb_map.setdefault(d.company, {}).setdefault(d.item_code, {}).\
|
||||
setdefault(d.warehouse, frappe._dict({\
|
||||
"opening_qty": 0.0, "opening_val": 0.0,
|
||||
"in_qty": 0.0, "in_val": 0.0,
|
||||
"out_qty": 0.0, "out_val": 0.0,
|
||||
"bal_qty": 0.0, "bal_val": 0.0,
|
||||
"opening_qty": 0.0, "opening_val": 0.0,
|
||||
"in_qty": 0.0, "in_val": 0.0,
|
||||
"out_qty": 0.0, "out_val": 0.0,
|
||||
"bal_qty": 0.0, "bal_val": 0.0,
|
||||
"val_rate": 0.0, "uom": None
|
||||
}))
|
||||
qty_dict = iwb_map[d.company][d.item_code][d.warehouse]
|
||||
qty_dict.uom = d.stock_uom
|
||||
|
||||
if d.voucher_type == "Stock Reconciliation":
|
||||
qty_diff = flt(d.qty_after_transaction) - qty_dict.bal_qty
|
||||
else:
|
||||
qty_diff = flt(d.actual_qty)
|
||||
|
||||
value_diff = flt(d.stock_value_difference)
|
||||
|
||||
if d.posting_date < filters["from_date"]:
|
||||
qty_dict.opening_qty += flt(d.actual_qty)
|
||||
qty_dict.opening_val += flt(d.actual_qty * d.valuation_rate)
|
||||
qty_dict.opening_qty += qty_diff
|
||||
qty_dict.opening_val += value_diff
|
||||
elif d.posting_date >= filters["from_date"] and d.posting_date <= filters["to_date"]:
|
||||
qty_dict.val_rate = d.valuation_rate
|
||||
|
||||
if flt(d.actual_qty) > 0:
|
||||
qty_dict.in_qty += flt(d.actual_qty)
|
||||
qty_dict.in_val += flt(d.actual_qty * d.valuation_rate)
|
||||
if qty_diff > 0:
|
||||
qty_dict.in_qty += qty_diff
|
||||
qty_dict.in_val += value_diff
|
||||
else:
|
||||
qty_dict.out_qty += abs(flt(d.actual_qty))
|
||||
qty_dict.out_val += flt(abs(flt(d.actual_qty)) * d.valuation_rate)
|
||||
qty_dict.out_qty += abs(qty_diff)
|
||||
qty_dict.out_val += abs(value_diff)
|
||||
|
||||
qty_dict.bal_qty += flt(d.actual_qty)
|
||||
qty_dict.bal_val += flt(d.actual_qty * d.valuation_rate)
|
||||
qty_dict.bal_qty += qty_diff
|
||||
qty_dict.bal_val += value_diff
|
||||
|
||||
return iwb_map
|
||||
|
||||
@@ -13,16 +13,13 @@ def execute(filters=None):
|
||||
data = []
|
||||
for sle in sl_entries:
|
||||
item_detail = item_details[sle.item_code]
|
||||
voucher_link_icon = """<a href="%s"><i class="icon icon-share"
|
||||
style="cursor: pointer;"></i></a>""" \
|
||||
% ("/".join(["#Form", sle.voucher_type, sle.voucher_no]),)
|
||||
|
||||
data.append([sle.date, sle.item_code, item_detail.item_name, item_detail.item_group,
|
||||
item_detail.brand, item_detail.description, sle.warehouse,
|
||||
item_detail.stock_uom, sle.actual_qty, sle.qty_after_transaction,
|
||||
(sle.incoming_rate if sle.actual_qty > 0 else 0.0),
|
||||
sle.valuation_rate, sle.stock_value, sle.voucher_type, sle.voucher_no,
|
||||
voucher_link_icon, sle.batch_no, sle.serial_no, sle.company])
|
||||
sle.batch_no, sle.serial_no, sle.company])
|
||||
|
||||
return columns, data
|
||||
|
||||
@@ -31,7 +28,7 @@ def get_columns():
|
||||
_("Brand") + ":Link/Brand:100", _("Description") + "::200", _("Warehouse") + ":Link/Warehouse:100",
|
||||
_("Stock UOM") + ":Link/UOM:100", _("Qty") + ":Float:50", _("Balance Qty") + ":Float:100",
|
||||
_("Incoming Rate") + ":Currency:110", _("Valuation Rate") + ":Currency:110", _("Balance Value") + ":Currency:110",
|
||||
_("Voucher Type") + "::110", _("Voucher #") + "::100", _("Link") + "::30", _("Batch") + ":Link/Batch:100",
|
||||
_("Voucher Type") + "::110", _("Voucher #") + ":Dynamic Link/Voucher Type:100", _("Batch") + ":Link/Batch:100",
|
||||
_("Serial #") + ":Link/Serial No:100", _("Company") + ":Link/Company:100"]
|
||||
|
||||
def get_stock_ledger_entries(filters):
|
||||
|
||||
@@ -27,7 +27,7 @@ def make_sl_entries(sl_entries, is_amended=None):
|
||||
if sle.get('is_cancelled') == 'Yes':
|
||||
sle['actual_qty'] = -flt(sle['actual_qty'])
|
||||
|
||||
if sle.get("actual_qty"):
|
||||
if sle.get("actual_qty") or sle.voucher_type=="Stock Reconciliation":
|
||||
sle_id = make_entry(sle)
|
||||
|
||||
args = sle.copy()
|
||||
@@ -36,9 +36,9 @@ def make_sl_entries(sl_entries, is_amended=None):
|
||||
"is_amended": is_amended
|
||||
})
|
||||
update_bin(args)
|
||||
|
||||
if cancel:
|
||||
delete_cancelled_entry(sl_entries[0].get('voucher_type'),
|
||||
sl_entries[0].get('voucher_no'))
|
||||
delete_cancelled_entry(sl_entries[0].get('voucher_type'), sl_entries[0].get('voucher_no'))
|
||||
|
||||
def set_as_cancel(voucher_type, voucher_no):
|
||||
frappe.db.sql("""update `tabStock Ledger Entry` set is_cancelled='Yes',
|
||||
@@ -58,7 +58,7 @@ def delete_cancelled_entry(voucher_type, voucher_no):
|
||||
frappe.db.sql("""delete from `tabStock Ledger Entry`
|
||||
where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no))
|
||||
|
||||
def update_entries_after(args, verbose=1):
|
||||
def update_entries_after(args, allow_zero_rate=False, verbose=1):
|
||||
"""
|
||||
update valution rate and qty after transaction
|
||||
from the current time-bucket onwards
|
||||
@@ -83,7 +83,6 @@ def update_entries_after(args, verbose=1):
|
||||
|
||||
entries_to_fix = get_sle_after_datetime(previous_sle or \
|
||||
{"item_code": args["item_code"], "warehouse": args["warehouse"]}, for_update=True)
|
||||
|
||||
valuation_method = get_valuation_method(args["item_code"])
|
||||
stock_value_difference = 0.0
|
||||
|
||||
@@ -95,21 +94,30 @@ def update_entries_after(args, verbose=1):
|
||||
qty_after_transaction += flt(sle.actual_qty)
|
||||
continue
|
||||
|
||||
|
||||
if sle.serial_no:
|
||||
valuation_rate = get_serialized_values(qty_after_transaction, sle, valuation_rate)
|
||||
elif valuation_method == "Moving Average":
|
||||
valuation_rate = get_moving_average_values(qty_after_transaction, sle, valuation_rate)
|
||||
else:
|
||||
valuation_rate = get_fifo_values(qty_after_transaction, sle, stock_queue)
|
||||
qty_after_transaction += flt(sle.actual_qty)
|
||||
|
||||
qty_after_transaction += flt(sle.actual_qty)
|
||||
else:
|
||||
if sle.voucher_type=="Stock Reconciliation":
|
||||
valuation_rate = sle.valuation_rate
|
||||
qty_after_transaction = sle.qty_after_transaction
|
||||
stock_queue = [[qty_after_transaction, valuation_rate]]
|
||||
else:
|
||||
if valuation_method == "Moving Average":
|
||||
valuation_rate = get_moving_average_values(qty_after_transaction, sle, valuation_rate, allow_zero_rate)
|
||||
else:
|
||||
valuation_rate = get_fifo_values(qty_after_transaction, sle, stock_queue, allow_zero_rate)
|
||||
|
||||
|
||||
qty_after_transaction += flt(sle.actual_qty)
|
||||
|
||||
# get stock value
|
||||
if sle.serial_no:
|
||||
stock_value = qty_after_transaction * valuation_rate
|
||||
elif valuation_method == "Moving Average":
|
||||
stock_value = (qty_after_transaction > 0) and \
|
||||
(qty_after_transaction * valuation_rate) or 0
|
||||
stock_value = qty_after_transaction * valuation_rate
|
||||
else:
|
||||
stock_value = sum((flt(batch[0]) * flt(batch[1]) for batch in stock_queue))
|
||||
|
||||
@@ -243,69 +251,72 @@ def get_serialized_values(qty_after_transaction, sle, valuation_rate):
|
||||
|
||||
return valuation_rate
|
||||
|
||||
def get_moving_average_values(qty_after_transaction, sle, valuation_rate):
|
||||
def get_moving_average_values(qty_after_transaction, sle, valuation_rate, allow_zero_rate):
|
||||
incoming_rate = flt(sle.incoming_rate)
|
||||
actual_qty = flt(sle.actual_qty)
|
||||
|
||||
if not incoming_rate:
|
||||
# In case of delivery/stock issue in_rate = 0 or wrong incoming rate
|
||||
incoming_rate = valuation_rate
|
||||
if flt(sle.actual_qty) > 0:
|
||||
if qty_after_transaction < 0 and not valuation_rate:
|
||||
# if negative stock, take current valuation rate as incoming rate
|
||||
valuation_rate = incoming_rate
|
||||
|
||||
elif qty_after_transaction < 0:
|
||||
# if negative stock, take current valuation rate as incoming rate
|
||||
valuation_rate = incoming_rate
|
||||
new_stock_qty = abs(qty_after_transaction) + actual_qty
|
||||
new_stock_value = (abs(qty_after_transaction) * valuation_rate) + (actual_qty * incoming_rate)
|
||||
|
||||
new_stock_qty = qty_after_transaction + actual_qty
|
||||
new_stock_value = qty_after_transaction * valuation_rate + actual_qty * incoming_rate
|
||||
if new_stock_qty:
|
||||
valuation_rate = new_stock_value / flt(new_stock_qty)
|
||||
elif not valuation_rate and qty_after_transaction <= 0:
|
||||
valuation_rate = get_valuation_rate(sle.item_code, sle.warehouse, allow_zero_rate)
|
||||
|
||||
if new_stock_qty > 0 and new_stock_value > 0:
|
||||
valuation_rate = new_stock_value / flt(new_stock_qty)
|
||||
elif new_stock_qty <= 0:
|
||||
valuation_rate = 0.0
|
||||
return abs(flt(valuation_rate))
|
||||
|
||||
# NOTE: val_rate is same as previous entry if new stock value is negative
|
||||
|
||||
return valuation_rate
|
||||
|
||||
def get_fifo_values(qty_after_transaction, sle, stock_queue):
|
||||
def get_fifo_values(qty_after_transaction, sle, stock_queue, allow_zero_rate):
|
||||
incoming_rate = flt(sle.incoming_rate)
|
||||
actual_qty = flt(sle.actual_qty)
|
||||
if not stock_queue:
|
||||
stock_queue.append([0, 0])
|
||||
|
||||
if actual_qty > 0:
|
||||
if not stock_queue:
|
||||
stock_queue.append([0, 0])
|
||||
|
||||
if stock_queue[-1][0] > 0:
|
||||
stock_queue.append([actual_qty, incoming_rate])
|
||||
else:
|
||||
qty = stock_queue[-1][0] + actual_qty
|
||||
stock_queue[-1] = [qty, qty > 0 and incoming_rate or 0]
|
||||
if qty == 0:
|
||||
stock_queue.pop(-1)
|
||||
else:
|
||||
stock_queue[-1] = [qty, incoming_rate]
|
||||
else:
|
||||
incoming_cost = 0
|
||||
qty_to_pop = abs(actual_qty)
|
||||
while qty_to_pop:
|
||||
if not stock_queue:
|
||||
stock_queue.append([0, 0])
|
||||
stock_queue.append([0, get_valuation_rate(sle.item_code, sle.warehouse, allow_zero_rate)
|
||||
if qty_after_transaction <= 0 else 0])
|
||||
|
||||
batch = stock_queue[0]
|
||||
|
||||
if 0 < batch[0] <= qty_to_pop:
|
||||
# if batch qty > 0
|
||||
# not enough or exactly same qty in current batch, clear batch
|
||||
incoming_cost += flt(batch[0]) * flt(batch[1])
|
||||
qty_to_pop -= batch[0]
|
||||
if qty_to_pop >= batch[0]:
|
||||
# consume current batch
|
||||
qty_to_pop = qty_to_pop - batch[0]
|
||||
stock_queue.pop(0)
|
||||
if not stock_queue and qty_to_pop:
|
||||
# stock finished, qty still remains to be withdrawn
|
||||
# negative stock, keep in as a negative batch
|
||||
stock_queue.append([-qty_to_pop, batch[1]])
|
||||
break
|
||||
|
||||
else:
|
||||
# all from current batch
|
||||
incoming_cost += flt(qty_to_pop) * flt(batch[1])
|
||||
batch[0] -= qty_to_pop
|
||||
# qty found in current batch
|
||||
# consume it and exit
|
||||
batch[0] = batch[0] - qty_to_pop
|
||||
qty_to_pop = 0
|
||||
|
||||
stock_value = sum((flt(batch[0]) * flt(batch[1]) for batch in stock_queue))
|
||||
stock_qty = sum((flt(batch[0]) for batch in stock_queue))
|
||||
|
||||
valuation_rate = stock_qty and (stock_value / flt(stock_qty)) or 0
|
||||
valuation_rate = (stock_value / flt(stock_qty)) if stock_qty else 0
|
||||
|
||||
return valuation_rate
|
||||
return abs(valuation_rate)
|
||||
|
||||
def _raise_exceptions(args, verbose=1):
|
||||
deficiency = min(e["diff"] for e in _exceptions)
|
||||
@@ -337,3 +348,26 @@ def get_previous_sle(args, for_update=False):
|
||||
"timestamp(posting_date, posting_time) <= timestamp(%(posting_date)s, %(posting_time)s)"],
|
||||
"desc", "limit 1", for_update=for_update)
|
||||
return sle and sle[0] or {}
|
||||
|
||||
def get_valuation_rate(item_code, warehouse, allow_zero_rate=False):
|
||||
last_valuation_rate = frappe.db.sql("""select valuation_rate
|
||||
from `tabStock Ledger Entry`
|
||||
where item_code = %s and warehouse = %s
|
||||
and ifnull(valuation_rate, 0) > 0
|
||||
order by posting_date desc, posting_time desc, name desc limit 1""", (item_code, warehouse))
|
||||
|
||||
if not last_valuation_rate:
|
||||
last_valuation_rate = frappe.db.sql("""select valuation_rate
|
||||
from `tabStock Ledger Entry`
|
||||
where item_code = %s and ifnull(valuation_rate, 0) > 0
|
||||
order by posting_date desc, posting_time desc, name desc limit 1""", item_code)
|
||||
|
||||
valuation_rate = flt(last_valuation_rate[0][0]) if last_valuation_rate else 0
|
||||
|
||||
if not valuation_rate:
|
||||
valuation_rate = frappe.db.get_value("Item Price", {"item_code": item_code, "buying": 1}, "price_list_rate")
|
||||
|
||||
if not allow_zero_rate and not valuation_rate and cint(frappe.db.get_value("Accounts Settings", None, "auto_accounting_for_stock")):
|
||||
frappe.throw(_("Purchase rate for item: {0} not found, which is required to book accounting entry (expense). Please mention item price against a buying price list.").format(item_code))
|
||||
|
||||
return valuation_rate
|
||||
|
||||
@@ -5,7 +5,6 @@ import frappe
|
||||
from frappe import _
|
||||
import json
|
||||
from frappe.utils import flt, cstr, nowdate, add_days, cint
|
||||
from frappe.defaults import get_global_default
|
||||
from frappe.utils.email_lib import sendmail
|
||||
from erpnext.accounts.utils import get_fiscal_year, FiscalYearError
|
||||
|
||||
@@ -94,7 +93,7 @@ def get_valuation_method(item_code):
|
||||
"""get valuation method from item or default"""
|
||||
val_method = frappe.db.get_value('Item', item_code, 'valuation_method')
|
||||
if not val_method:
|
||||
val_method = get_global_default('valuation_method') or "FIFO"
|
||||
val_method = frappe.db.get_value("Stock Settings", None, "valuation_method") or "FIFO"
|
||||
return val_method
|
||||
|
||||
def get_fifo_rate(previous_stock_queue, qty):
|
||||
|
||||
@@ -279,7 +279,7 @@
|
||||
"icon": "icon-file-text",
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"modified": "2014-06-23 07:55:49.200714",
|
||||
"modified": "2014-09-26 11:37:41.026433",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Support",
|
||||
"name": "Maintenance Visit",
|
||||
@@ -301,7 +301,7 @@
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"search_fields": "status,maintenance_type,customer,customer_name, address,mntc_date,company,fiscal_year",
|
||||
"search_fields": "status,maintenance_type,customer,customer_name,mntc_date,company,fiscal_year",
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<h2>Recurring {{ type }} Failed</h2>
|
||||
|
||||
<p>An error occured while creating recurring {{ type }} <b>{{ name }}</b> for <b>{{ customer }}</b>.</p>
|
||||
<p>An error occured while creating recurring {{ type }} <b>{{ name }}</b> for <b>{{ party }}</b>.</p>
|
||||
<p>This could be because of some invalid email ids in the {{ type }}.</p>
|
||||
<p>To stop sending repetitive error notifications from the system, we have unchecked
|
||||
"Convert into Recurring" field in the {{ type }} {{ name }}.</p>
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"<a href=""#Sales Browser/Territory"">Add / Edit</a>","<a href=""#Sales Browser/Territory""> إضافة / تحرير < / A>"
|
||||
"<h4>Default Template</h4><p>Uses <a href=""http://jinja.pocoo.org/docs/templates/"">Jinja Templating</a> and all the fields of Address (including Custom Fields if any) will be available</p><pre><code>{{ address_line1 }}<br>{% if address_line2 %}{{ address_line2 }}<br>{% endif -%}{{ city }}<br>{% if state %}{{ state }}<br>{% endif -%}{% if pincode %} PIN: {{ pincode }}<br>{% endif -%}{{ country }}<br>{% if phone %}Phone: {{ phone }}<br>{% endif -%}{% if fax %}Fax: {{ fax }}<br>{% endif -%}{% if email_id %}Email: {{ email_id }}<br>{% endif -%}</code></pre>","<h4> افتراضي قالب </ H4> <p> ويستخدم <a href=""http://jinja.pocoo.org/docs/templates/""> جنجا القولبة </ a> و كافة الحقول من العنوان ( بما في ذلك الحقول المخصصة إن وجدت) وسوف تكون متاحة </ P> <PRE> على <code> {{}} address_line1 <BR> {٪ إذا address_line2٪} {{}} address_line2 <BR> { ENDIF٪ -٪} {{المدينة}} <BR> {٪ إذا الدولة٪} {{الدولة}} {<BR>٪ ENDIF -٪} {٪ إذا كان الرقم السري٪} PIN: {{}} الرقم السري {<BR>٪ ENDIF -٪} {{البلد}} <BR> {٪ إذا كان الهاتف٪} الهاتف: {{هاتف}} {<BR> ENDIF٪ -٪} {٪ إذا الفاكس٪} فاكس: {{}} الفاكس <BR> {٪ ENDIF -٪} {٪٪ إذا email_id} البريد الإلكتروني: {{}} email_id <BR> ؛ {٪ ENDIF -٪} </ رمز> </ قبل>"
|
||||
A Customer Group exists with same name please change the Customer name or rename the Customer Group,يوجد مجموعة العملاء مع نفس الاسم الرجاء تغيير اسم العميل أو إعادة تسمية المجموعة العملاء
|
||||
A Customer exists with same name,العملاء من وجود نفس الاسم مع
|
||||
A Customer exists with same name,يوجد في قائمة العملاء عميل بنفس الاسم
|
||||
A Lead with this email id should exist,وينبغي أن يكون هذا المعرف الرصاص مع البريد الإلكتروني موجود
|
||||
A Product or Service,منتج أو خدمة
|
||||
A Supplier exists with same name,وهناك مورد موجود مع نفس الاسم
|
||||
@@ -47,7 +47,7 @@ Absent,غائب
|
||||
Acceptance Criteria,معايير القبول
|
||||
Accepted,مقبول
|
||||
Accepted + Rejected Qty must be equal to Received quantity for Item {0},يجب أن يكون مقبول مرفوض + الكمية مساوية ل كمية تلقى القطعة ل {0}
|
||||
Accepted Quantity,قبلت الكمية
|
||||
Accepted Quantity,كمية مقبولة
|
||||
Accepted Warehouse,قبلت مستودع
|
||||
Account,حساب
|
||||
Account Balance,رصيد حسابك
|
||||
@@ -65,12 +65,12 @@ Account with child nodes cannot be converted to ledger,حساب مع العقد
|
||||
Account with existing transaction can not be converted to group.,حساب مع الصفقة الحالية لا يمكن تحويلها إلى المجموعة.
|
||||
Account with existing transaction can not be deleted,حساب مع الصفقة الحالية لا يمكن حذف
|
||||
Account with existing transaction cannot be converted to ledger,حساب مع الصفقة الحالية لا يمكن تحويلها إلى دفتر الأستاذ
|
||||
Account {0} cannot be a Group,حساب {0} لا يمكن أن تكون المجموعة
|
||||
Account {0} does not belong to Company {1},حساب {0} لا تنتمي إلى شركة {1}
|
||||
Account {0} cannot be a Group,حساب {0} لا يمكن أن يكون مجموعة
|
||||
Account {0} does not belong to Company {1},حساب {0} لا ينتمي إلى شركة {1}
|
||||
Account {0} does not belong to company: {1},حساب {0} لا تنتمي إلى الشركة: {1}
|
||||
Account {0} does not exist,حساب {0} غير موجود
|
||||
Account {0} has been entered more than once for fiscal year {1},حساب {0} تم إدخال أكثر من مرة للعام المالي {1}
|
||||
Account {0} is frozen,حساب {0} يتم تجميد
|
||||
Account {0} is frozen,حساب {0} مجمد
|
||||
Account {0} is inactive,حساب {0} غير نشط
|
||||
Account {0} is not valid,حساب {0} غير صالح
|
||||
Account {0} must be of type 'Fixed Asset' as Item {1} is an Asset Item,"حساب {0} يجب أن تكون من النوع ' الأصول الثابتة ""كما البند {1} هو البند الأصول"
|
||||
@@ -85,7 +85,7 @@ Accounting,المحاسبة
|
||||
"Accounting entry frozen up to this date, nobody can do / modify entry except role specified below.",قيد محاسبي المجمدة تصل إلى هذا التاريخ، لا أحد يمكن أن تفعل / تعديل إدخال باستثناء دور المحددة أدناه.
|
||||
Accounting journal entries.,المحاسبة إدخالات دفتر اليومية.
|
||||
Accounts,حسابات
|
||||
Accounts Browser,حسابات متصفح
|
||||
Accounts Browser,متصفح الحسابات
|
||||
Accounts Frozen Upto,حسابات مجمدة لغاية
|
||||
Accounts Payable,ذمم دائنة
|
||||
Accounts Receivable,حسابات القبض
|
||||
@@ -95,13 +95,13 @@ Active: Will extract emails from ,نشط: سيتم استخراج رسائل ا
|
||||
Activity,نشاط
|
||||
Activity Log,سجل النشاط
|
||||
Activity Log:,النشاط المفتاح:
|
||||
Activity Type,النشاط نوع
|
||||
Activity Type,نوع النشاط
|
||||
Actual,فعلي
|
||||
Actual Budget,الميزانية الفعلية
|
||||
Actual Completion Date,تاريخ الإنتهاء الفعلي
|
||||
Actual Date,تاريخ الفعلية
|
||||
Actual Date,التاريخ الفعلي
|
||||
Actual End Date,تاريخ الإنتهاء الفعلي
|
||||
Actual Invoice Date,الفعلي تاريخ الفاتورة
|
||||
Actual Invoice Date,التاريخ الفعلي للفاتورة
|
||||
Actual Posting Date,تاريخ النشر الفعلي
|
||||
Actual Qty,الكمية الفعلية
|
||||
Actual Qty (at source/target),الكمية الفعلية (في المصدر / الهدف)
|
||||
@@ -112,7 +112,7 @@ Actual Start Date,تاريخ البدء الفعلي
|
||||
Add,إضافة
|
||||
Add / Edit Taxes and Charges,إضافة / تعديل الضرائب والرسوم
|
||||
Add Child,إضافة الطفل
|
||||
Add Serial No,إضافة رقم المسلسل
|
||||
Add Serial No,إضافة رقم تسلسلي
|
||||
Add Taxes,إضافة الضرائب
|
||||
Add Taxes and Charges,إضافة الضرائب والرسوم
|
||||
Add or Deduct,إضافة أو خصم
|
||||
@@ -131,7 +131,7 @@ Address Line 2,العنوان سطر 2
|
||||
Address Template,قالب عنوان
|
||||
Address Title,عنوان عنوان
|
||||
Address Title is mandatory.,عنوان عنوانها إلزامية.
|
||||
Address Type,عنوان نوع
|
||||
Address Type,نوع العنوان
|
||||
Address master.,عنوان رئيسي.
|
||||
Administrative Expenses,المصاريف الإدارية
|
||||
Administrative Officer,موظف إداري
|
||||
@@ -217,7 +217,7 @@ Amount to Bill,تصل إلى بيل
|
||||
An Customer exists with same name,موجود على العملاء مع نفس الاسم
|
||||
"An Item Group exists with same name, please change the item name or rename the item group",وجود فريق المدينة مع نفس الاسم، الرجاء تغيير اسم العنصر أو إعادة تسمية المجموعة البند
|
||||
"An item exists with same name ({0}), please change the item group name or rename the item",عنصر موجود مع نفس الاسم ( {0} ) ، الرجاء تغيير اسم المجموعة البند أو إعادة تسمية هذا البند
|
||||
Analyst,المحلل
|
||||
Analyst,محلل
|
||||
Annual,سنوي
|
||||
Another Period Closing Entry {0} has been made after {1},دخول أخرى الفترة الإنتهاء {0} أحرز بعد {1}
|
||||
Another Salary Structure {0} is active for employee {0}. Please make its status 'Inactive' to proceed.,"هيكل الرواتب أخرى {0} نشطة للموظف {0} . يرجى التأكد مكانتها ""غير نشطة "" والمضي قدما."
|
||||
@@ -266,15 +266,15 @@ Atleast one of the Selling or Buying must be selected,يجب تحديد الاق
|
||||
Atleast one warehouse is mandatory,واحدة على الاقل مستودع إلزامي
|
||||
Attach Image,إرفاق صورة
|
||||
Attach Letterhead,نعلق رأسية
|
||||
Attach Logo,نعلق شعار
|
||||
Attach Your Picture,نعلق صورتك
|
||||
Attach Logo,إرفاق صورة الشعار/العلامة التجارية
|
||||
Attach Your Picture,إرفاق صورتك
|
||||
Attendance,الحضور
|
||||
Attendance Date,تاريخ الحضور
|
||||
Attendance Details,تفاصيل الحضور
|
||||
Attendance From Date,الحضور من تاريخ
|
||||
Attendance From Date and Attendance To Date is mandatory,الحضور من التسجيل والحضور إلى تاريخ إلزامي
|
||||
Attendance To Date,الحضور إلى تاريخ
|
||||
Attendance can not be marked for future dates,لا يمكن أن تكون علامة لحضور تواريخ مستقبلية
|
||||
Attendance can not be marked for future dates,لا يمكن أن ىكون تاريخ الحضور تاريخ مستقبلي
|
||||
Attendance for employee {0} is already marked,الحضور للموظف {0} تم وضع علامة بالفعل
|
||||
Attendance record.,سجل الحضور.
|
||||
Authorization Control,إذن التحكم
|
||||
@@ -287,13 +287,13 @@ Automatically extract Job Applicants from a mail box ,
|
||||
Automatically extract Leads from a mail box e.g.,استخراج الشراء تلقائيا من صندوق البريد على سبيل المثال
|
||||
Automatically updated via Stock Entry of type Manufacture/Repack,تحديثها تلقائيا عن طريق إدخال الأسهم الصنع نوع / أعد حزم
|
||||
Automotive,السيارات
|
||||
Autoreply when a new mail is received,عندما رد تلقائي تلقي بريد جديد
|
||||
Autoreply when a new mail is received,رد تلقائي عند تلقي بريد جديد
|
||||
Available,متاح
|
||||
Available Qty at Warehouse,الكمية المتاحة في مستودع
|
||||
Available Stock for Packing Items,الأسهم المتاحة للتعبئة وحدات
|
||||
"Available in BOM, Delivery Note, Purchase Invoice, Production Order, Purchase Order, Purchase Receipt, Sales Invoice, Sales Order, Stock Entry, Timesheet",المتاحة في BOM ، تسليم مذكرة ، شراء الفاتورة ، ترتيب الإنتاج، طلب شراء ، شراء استلام ، فاتورة المبيعات ، ترتيب المبيعات ، اسهم الدخول و الجدول الزمني
|
||||
Average Age,متوسط العمر
|
||||
Average Commission Rate,متوسط سعر جنة
|
||||
Average Commission Rate,متوسط العمولة
|
||||
Average Discount,متوسط الخصم
|
||||
Awesome Products,المنتجات رهيبة
|
||||
Awesome Services,خدمات رهيبة
|
||||
@@ -331,9 +331,9 @@ Bank Clearance Summary,بنك ملخص التخليص
|
||||
Bank Draft,البنك مشروع
|
||||
Bank Name,اسم البنك
|
||||
Bank Overdraft Account,حساب السحب على المكشوف المصرفي
|
||||
Bank Reconciliation,البنك المصالحة
|
||||
Bank Reconciliation Detail,البنك المصالحة تفاصيل
|
||||
Bank Reconciliation Statement,بيان التسويات المصرفية
|
||||
Bank Reconciliation,تسوية البنك
|
||||
Bank Reconciliation Detail,تفاصيل تسوية البنك
|
||||
Bank Reconciliation Statement,بيان تسوية البنك
|
||||
Bank Voucher,البنك قسيمة
|
||||
Bank/Cash Balance,بنك / النقد وما في حكمه
|
||||
Banking,مصرفي
|
||||
@@ -405,8 +405,8 @@ Bundle items at time of sale.,حزمة البنود في وقت البيع.
|
||||
Business Development Manager,مدير تطوير الأعمال
|
||||
Buying,شراء
|
||||
Buying & Selling,شراء وبيع
|
||||
Buying Amount,شراء المبلغ
|
||||
Buying Settings,شراء إعدادات
|
||||
Buying Amount,مبلغ الشراء
|
||||
Buying Settings,إعدادات الشراء
|
||||
"Buying must be checked, if Applicable For is selected as {0}",يجب أن يتم التحقق الشراء، إذا تم تحديد مطبق للك {0}
|
||||
C-Form,نموذج C-
|
||||
C-Form Applicable,C-نموذج قابل للتطبيق
|
||||
@@ -1025,7 +1025,7 @@ Extract Emails,استخراج رسائل البريد الإلكتروني
|
||||
FCFS Rate,FCFS قيم
|
||||
Failed: ,فشل:
|
||||
Family Background,الخلفية العائلية
|
||||
Fax,بالفاكس
|
||||
Fax,فاكس
|
||||
Features Setup,ميزات الإعداد
|
||||
Feed,أطعم
|
||||
Feed Type,إطعام نوع
|
||||
@@ -1041,7 +1041,7 @@ Financial / accounting year.,المالية / المحاسبة العام.
|
||||
Financial Analytics,تحليلات مالية
|
||||
Financial Services,الخدمات المالية
|
||||
Financial Year End Date,تاريخ نهاية السنة المالية
|
||||
Financial Year Start Date,السنة المالية تاريخ بدء
|
||||
Financial Year Start Date,تاريخ بدء السنة المالية
|
||||
Finished Goods,السلع تامة الصنع
|
||||
First Name,الاسم الأول
|
||||
First Responded On,أجاب أولا على
|
||||
@@ -1059,7 +1059,7 @@ Food,غذاء
|
||||
For Company,لشركة
|
||||
For Employee,لموظف
|
||||
For Employee Name,لاسم الموظف
|
||||
For Price List,ل ائحة الأسعار
|
||||
For Price List,لائحة الأسعار
|
||||
For Production,للإنتاج
|
||||
For Reference Only.,للإشارة فقط.
|
||||
For Sales Invoice,لفاتورة المبيعات
|
||||
@@ -1823,7 +1823,7 @@ Notify by Email on creation of automatic Material Request,إبلاغ عن طري
|
||||
Number Format,عدد تنسيق
|
||||
Offer Date,عرض التسجيل
|
||||
Office,مكتب
|
||||
Office Equipments,معدات المكاتب
|
||||
Office Equipments,أدوات المكتب
|
||||
Office Maintenance Expenses,مصاريف صيانة المكاتب
|
||||
Office Rent,مكتب للإيجار
|
||||
Old Parent,العمر الرئيسي
|
||||
@@ -1840,7 +1840,7 @@ Open Production Orders,أوامر مفتوحة الانتاج
|
||||
Open Tickets,تذاكر مفتوحة
|
||||
Opening (Cr),افتتاح (الكروم )
|
||||
Opening (Dr),افتتاح ( الدكتور )
|
||||
Opening Date,فتح تاريخ
|
||||
Opening Date,تاريخ الفتح
|
||||
Opening Entry,فتح دخول
|
||||
Opening Qty,فتح الكمية
|
||||
Opening Time,يفتح من الساعة
|
||||
@@ -1854,7 +1854,7 @@ Operation {0} is repeated in Operations Table,عملية {0} يتكرر في ج
|
||||
Operation {0} not present in Operations Table,عملية {0} غير موجودة في جدول العمليات
|
||||
Operations,عمليات
|
||||
Opportunity,فرصة
|
||||
Opportunity Date,الفرصة تاريخ
|
||||
Opportunity Date,تاريخ الفرصة
|
||||
Opportunity From,فرصة من
|
||||
Opportunity Item,فرصة السلعة
|
||||
Opportunity Items,فرصة الأصناف
|
||||
@@ -1914,9 +1914,9 @@ Packing Slip,زلة التعبئة
|
||||
Packing Slip Item,التعبئة الإغلاق زلة
|
||||
Packing Slip Items,التعبئة عناصر زلة
|
||||
Packing Slip(s) cancelled,زلة التعبئة (ق ) إلغاء
|
||||
Page Break,الصفحة استراحة
|
||||
Page Name,الصفحة اسم
|
||||
Paid Amount,دفع المبلغ
|
||||
Page Break,فاصل الصفحة
|
||||
Page Name,اسم الصفحة
|
||||
Paid Amount,المبلغ المدفوع
|
||||
Paid amount + Write Off Amount can not be greater than Grand Total,المبلغ المدفوع + شطب المبلغ لا يمكن أن يكون أكبر من المجموع الكلي
|
||||
Pair,زوج
|
||||
Parameter,المعلمة
|
||||
@@ -3128,7 +3128,7 @@ Users with this role are allowed to create / modify accounting entry before froz
|
||||
Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts,يسمح للمستخدمين مع هذا الدور لضبط الحسابات المجمدة و إنشاء / تعديل القيود المحاسبية على حسابات مجمدة
|
||||
Utilities,خدمات
|
||||
Utility Expenses,مصاريف فائدة
|
||||
Valid For Territories,صالحة للالأقاليم
|
||||
Valid For Territories,صالحة للأقاليم
|
||||
Valid From,صالحة من
|
||||
Valid Upto,صالحة لغاية
|
||||
Valid for Territories,صالحة للالأقاليم
|
||||
@@ -3237,7 +3237,7 @@ Write Off Voucher,شطب قسيمة
|
||||
Wrong Template: Unable to find head row.,قالب الخطأ: تعذر العثور على صف الرأس.
|
||||
Year,عام
|
||||
Year Closed,مغلق العام
|
||||
Year End Date,نهاية التاريخ العام
|
||||
Year End Date,تاريخ نهاية العام
|
||||
Year Name,اسم العام
|
||||
Year Start Date,تاريخ بدء العام
|
||||
Year of Passing,اجتياز سنة
|
||||
@@ -3261,8 +3261,8 @@ You have entered duplicate items. Please rectify and try again.,لقد دخلت
|
||||
You may need to update: {0},قد تحتاج إلى تحديث : {0}
|
||||
You must Save the form before proceeding,يجب حفظ النموذج قبل الشروع
|
||||
Your Customer's TAX registration numbers (if applicable) or any general information,عميلك أرقام التسجيل الضريبي (إن وجدت) أو أي معلومات عامة
|
||||
Your Customers,الزبائن
|
||||
Your Login Id,تسجيل الدخول اسم المستخدم الخاص بك
|
||||
Your Customers,العملاء
|
||||
Your Login Id,تسجيل الدخول - اسم المستخدم الخاص بك
|
||||
Your Products or Services,المنتجات أو الخدمات الخاصة بك
|
||||
Your Suppliers,لديك موردون
|
||||
Your email address,عنوان البريد الإلكتروني الخاص بك
|
||||
|
||||
|
@@ -41,8 +41,8 @@ A Product or Service,Ένα Προϊόν ή Υπηρεσία
|
||||
A Supplier exists with same name,Ένας προμηθευτής υπάρχει με το ίδιο όνομα
|
||||
A symbol for this currency. For e.g. $,Ένα σύμβολο για το νόμισμα αυτό. Για παράδειγμα $
|
||||
AMC Expiry Date,AMC Ημερομηνία Λήξης
|
||||
Abbr,Abbr
|
||||
Abbreviation cannot have more than 5 characters,Συντομογραφία δεν μπορεί να έχει περισσότερα από 5 χαρακτήρες
|
||||
Abbr,Συντ.
|
||||
Abbreviation cannot have more than 5 characters,Μια συντομογραφία δεν μπορεί να έχει περισσότερα από 5 χαρακτήρες
|
||||
Above Value,Πάνω Value
|
||||
Absent,Απών
|
||||
Acceptance Criteria,Κριτήρια αποδοχής
|
||||
@@ -50,9 +50,9 @@ Accepted,Δεκτός
|
||||
Accepted + Rejected Qty must be equal to Received quantity for Item {0},Αποδεκτές + Απορρίπτεται Ποσότητα πρέπει να είναι ίση με Ελήφθη ποσότητα για τη θέση {0}
|
||||
Accepted Quantity,ΠΟΣΟΤΗΤΑ
|
||||
Accepted Warehouse,Αποδεκτές αποθήκη
|
||||
Account,λογαριασμός
|
||||
Account Balance,Υπόλοιπο λογαριασμού
|
||||
Account Created: {0},Ο λογαριασμός Δημιουργήθηκε : {0}
|
||||
Account,Λογαριασμός
|
||||
Account Balance,Υπόλοιπο Λογαριασμού
|
||||
Account Created: {0},Ο λογαριασμός δημιουργήθηκε : {0}
|
||||
Account Details,Στοιχεία Λογαριασμού
|
||||
Account Head,Επικεφαλής λογαριασμού
|
||||
Account Name,Όνομα λογαριασμού
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -35,41 +35,42 @@
|
||||
"<a href=""#Sales Browser/Territory"">Add / Edit</a>","<a href=""#Sales Browser/Territory""> Ajouter / Modifier < / a>"
|
||||
"<h4>Default Template</h4><p>Uses <a href=""http://jinja.pocoo.org/docs/templates/"">Jinja Templating</a> and all the fields of Address (including Custom Fields if any) will be available</p><pre><code>{{ address_line1 }}<br>{% if address_line2 %}{{ address_line2 }}<br>{% endif -%}{{ city }}<br>{% if state %}{{ state }}<br>{% endif -%}{% if pincode %} PIN: {{ pincode }}<br>{% endif -%}{{ country }}<br>{% if phone %}Phone: {{ phone }}<br>{% endif -%}{% if fax %}Fax: {{ fax }}<br>{% endif -%}{% if email_id %}Email: {{ email_id }}<br>{% endif -%}</code></pre>","<h4> modèle par défaut </ h4> <p> Utilise <a href=""http://jinja.pocoo.org/docs/templates/""> Jinja création de modèles </ a> et tous les domaines de l'Adresse ( y compris les champs personnalisés cas échéant) sera disponible </ p> <pre> <code> {{}} address_line1 Photos {% si address_line2%} {{}} address_line2 <br> { % endif -%} {{ville}} Photos {% si l'état%} {{état}} {% endif Photos -%} {% if%} code PIN PIN: {{code PIN}} {% endif Photos -%} {{pays}} Photos {% si le téléphone%} Téléphone: {{phone}} {<br> % endif -%} {% if%} fax Fax: {{fax}} {% endif Photos -%} {% if%} email_id Email: {{}} email_id Photos ; {% endif -%} </ code> </ pre>"
|
||||
A Customer Group exists with same name please change the Customer name or rename the Customer Group,BOM récursivité : {0} ne peut pas être le parent ou l'enfant de {2}
|
||||
A Customer exists with same name,Une clientèle existe avec le même nom
|
||||
A Lead with this email id should exist,Un responsable de cette id e-mail doit exister
|
||||
A Product or Service,Nouveau N ° de série ne peut pas avoir d'entrepôt . Entrepôt doit être réglé par Stock entrée ou ticket de caisse
|
||||
A Supplier exists with same name,Un Fournisseur existe avec le même nom
|
||||
A symbol for this currency. For e.g. $,Un symbole de cette monnaie. Pour exemple $
|
||||
A Customer exists with same name,Un client existe avec le même nom
|
||||
A Lead with this email id should exist,Un responsable de cet identifiant de courriel doit exister
|
||||
A Product or Service,Un produit ou service
|
||||
A Supplier exists with same name,Un fournisseur existe avec ce même nom
|
||||
A symbol for this currency. For e.g. $,Un symbole pour cette monnaie. Par exemple $
|
||||
AMC Expiry Date,AMC Date d'expiration
|
||||
Abbr,Abbr
|
||||
Abbreviation cannot have more than 5 characters,Compte avec la transaction existante ne peut pas être converti en groupe.
|
||||
Abbreviation cannot have more than 5 characters,L'abbréviation ne peut pas avoir plus de 5 caractères
|
||||
Above Value,Au-dessus de la valeur
|
||||
Absent,Absent
|
||||
Acceptance Criteria,Critères d'acceptation
|
||||
Accepted,Accepté
|
||||
Accepted + Rejected Qty must be equal to Received quantity for Item {0},Compte {0} doit être SAMES comme débit pour tenir compte de la facture de vente en ligne {0}
|
||||
Accepted + Rejected Qty must be equal to Received quantity for Item {0},"La quantité acceptée + rejetée doit être égale à la quantité reçue pour l'Item {0}
|
||||
Compte {0} doit être SAMES comme débit pour tenir compte de la facture de vente en ligne {0}"
|
||||
Accepted Quantity,Quantité acceptés
|
||||
Accepted Warehouse,Entrepôt acceptés
|
||||
Accepted Warehouse,Entrepôt acceptable
|
||||
Account,compte
|
||||
Account Balance,Solde du compte
|
||||
Account Created: {0},Compte créé : {0}
|
||||
Account Details,Détails du compte
|
||||
Account Head,Chef du compte
|
||||
Account Head,Responsable du compte
|
||||
Account Name,Nom du compte
|
||||
Account Type,Type de compte
|
||||
"Account balance already in Credit, you are not allowed to set 'Balance Must Be' as 'Debit'","Le solde du compte déjà en crédit, vous n'êtes pas autorisé à mettre en 'équilibre doit être' comme 'débit'"
|
||||
"Account balance already in Debit, you are not allowed to set 'Balance Must Be' as 'Credit'","Le solde du compte déjà en débit, vous n'êtes pas autorisé à définir 'équilibre doit être' comme 'Crédit'"
|
||||
Account for the warehouse (Perpetual Inventory) will be created under this Account.,Compte de l'entrepôt ( de l'inventaire permanent ) sera créé sous ce compte .
|
||||
Account head {0} created,Employé soulagé sur {0} doit être défini comme «gauche»
|
||||
Account must be a balance sheet account,arrhes
|
||||
Account head {0} created,Responsable du compte {0} a été crée
|
||||
Account must be a balance sheet account,Le compte doit être un bilan
|
||||
Account with child nodes cannot be converted to ledger,Liste des prix non sélectionné
|
||||
Account with existing transaction can not be converted to group.,{0} n'est pas un congé approbateur valide
|
||||
Account with existing transaction can not be deleted,Compte avec la transaction existante ne peut pas être supprimé
|
||||
Account with existing transaction cannot be converted to ledger,Compte avec la transaction existante ne peut pas être converti en livre
|
||||
Account {0} cannot be a Group,Compte {0} ne peut pas être un groupe
|
||||
Account {0} does not belong to Company {1},{0} créé
|
||||
Account {0} does not belong to Company {1},Compte {0} n'appartient pas à la société {1}
|
||||
Account {0} does not belong to company: {1},Compte {0} n'appartient pas à l'entreprise: {1}
|
||||
Account {0} does not exist,Votre adresse e-mail
|
||||
Account {0} does not exist,Compte {0} n'existe pas
|
||||
Account {0} has been entered more than once for fiscal year {1},S'il vous plaît entrer « Répétez le jour du Mois de la« valeur de champ
|
||||
Account {0} is frozen,Attention: Commande {0} existe déjà contre le même numéro de bon de commande
|
||||
Account {0} is inactive,dépenses directes
|
||||
@@ -80,13 +81,13 @@ Account {0}: Parent account {1} does not belong to company: {2},Compte {0}: comp
|
||||
Account {0}: Parent account {1} does not exist,Compte {0}: compte de Parent {1} n'existe pas
|
||||
Account {0}: You can not assign itself as parent account,Compte {0}: Vous ne pouvez pas lui attribuer que compte parent
|
||||
Account: {0} can only be updated via \ Stock Transactions,Compte: {0} ne peut être mise à jour via \ Transactions de stock
|
||||
Accountant,comptable
|
||||
Accountant,Comptable
|
||||
Accounting,Comptabilité
|
||||
"Accounting Entries can be made against leaf nodes, called","Écritures comptables peuvent être faites contre nœuds feuilles , appelé"
|
||||
"Accounting entry frozen up to this date, nobody can do / modify entry except role specified below.","Saisie comptable gelé jusqu'à cette date, personne ne peut faire / modifier entrée sauf rôle spécifié ci-dessous."
|
||||
Accounting journal entries.,Les écritures comptables.
|
||||
Accounts,Comptes
|
||||
Accounts Browser,comptes navigateur
|
||||
Accounts Browser,Navigateur des comptes
|
||||
Accounts Frozen Upto,Jusqu'à comptes gelés
|
||||
Accounts Payable,Comptes à payer
|
||||
Accounts Receivable,Débiteurs
|
||||
@@ -113,28 +114,28 @@ Actual Start Date,Date de début réelle
|
||||
Add,Ajouter
|
||||
Add / Edit Taxes and Charges,Ajouter / Modifier Taxes et frais
|
||||
Add Child,Ajouter un enfant
|
||||
Add Serial No,Ajouter N ° de série
|
||||
Add Serial No,Ajouter Numéro de série
|
||||
Add Taxes,Ajouter impôts
|
||||
Add Taxes and Charges,Ajouter Taxes et frais
|
||||
Add or Deduct,Ajouter ou déduire
|
||||
Add rows to set annual budgets on Accounts.,Ajoutez des lignes d'établir des budgets annuels des comptes.
|
||||
Add to Cart,ERP open source construit pour le web
|
||||
Add to calendar on this date,Ajouter au calendrier à cette date
|
||||
Add rows to set annual budgets on Accounts.,Ajoutez des lignes pour établir des budgets annuels sur des comptes.
|
||||
Add to Cart,Ajouter au panier
|
||||
Add to calendar on this date,Ajouter cette date au calendrier
|
||||
Add/Remove Recipients,Ajouter / supprimer des destinataires
|
||||
Address,Adresse
|
||||
Address & Contact,Adresse et coordonnées
|
||||
Address & Contacts,Adresse & Contacts
|
||||
Address & Contacts,Adresse & Coordonnées
|
||||
Address Desc,Adresse Desc
|
||||
Address Details,Détails de l'adresse
|
||||
Address HTML,Adresse HTML
|
||||
Address Line 1,Adresse ligne 1
|
||||
Address Line 2,Adresse ligne 2
|
||||
Address Template,Adresse modèle
|
||||
Address Title,Titre Adresse
|
||||
Address Title,Titre de l'adresse
|
||||
Address Title is mandatory.,Vous n'êtes pas autorisé à imprimer ce document
|
||||
Address Type,Type d'adresse
|
||||
Address master.,Ou créés par
|
||||
Administrative Expenses,applicabilité
|
||||
Address master.,Adresse principale
|
||||
Administrative Expenses,Dépenses administratives
|
||||
Administrative Officer,de l'administration
|
||||
Advance Amount,Montant de l'avance
|
||||
Advance amount,Montant de l'avance
|
||||
@@ -142,7 +143,7 @@ Advances,Avances
|
||||
Advertisement,Publicité
|
||||
Advertising,publicité
|
||||
Aerospace,aérospatial
|
||||
After Sale Installations,Après Installations Vente
|
||||
After Sale Installations,Installations Après Vente
|
||||
Against,Contre
|
||||
Against Account,Contre compte
|
||||
Against Bill {0} dated {1},Courriel invalide : {0}
|
||||
@@ -197,7 +198,7 @@ Allocated amount can not greater than unadusted amount,Montant alloué ne peut p
|
||||
Allow Bill of Materials,Laissez Bill of Materials
|
||||
Allow Bill of Materials should be 'Yes'. Because one or many active BOMs present for this item,Commande {0} n'est pas valide
|
||||
Allow Children,permettre aux enfants
|
||||
Allow Dropbox Access,Autoriser l'accès Dropbox
|
||||
Allow Dropbox Access,Autoriser l'accès au Dropbox
|
||||
Allow Google Drive Access,Autoriser Google Drive accès
|
||||
Allow Negative Balance,Laissez solde négatif
|
||||
Allow Negative Stock,Laissez Stock Négatif
|
||||
@@ -219,7 +220,7 @@ An Customer exists with same name,Il existe un client avec le même nom
|
||||
"An Item Group exists with same name, please change the item name or rename the item group","Un groupe d'objet existe avec le même nom , s'il vous plaît changer le nom de l'élément ou de renommer le groupe de l'article"
|
||||
"An item exists with same name ({0}), please change the item group name or rename the item",Le compte doit être un compte de bilan
|
||||
Analyst,analyste
|
||||
Annual,Nomenclature
|
||||
Annual,Annuel
|
||||
Another Period Closing Entry {0} has been made after {1},Point Wise impôt Détail
|
||||
Another Salary Structure {0} is active for employee {0}. Please make its status 'Inactive' to proceed.,Le taux de conversion ne peut pas être égal à 0 ou 1
|
||||
"Any other comments, noteworthy effort that should go in the records.","D'autres commentaires, l'effort remarquable qui devrait aller dans les dossiers."
|
||||
@@ -288,20 +289,20 @@ Automatically extract Job Applicants from a mail box ,
|
||||
Automatically extract Leads from a mail box e.g.,Extraire automatiquement des prospects à partir d'une boîte aux lettres par exemple
|
||||
Automatically updated via Stock Entry of type Manufacture/Repack,Automatiquement mis à jour via l'entrée de fabrication de type Stock / Repack
|
||||
Automotive,automobile
|
||||
Autoreply when a new mail is received,Autoreply quand un nouveau message est reçu
|
||||
Autoreply when a new mail is received,Réponse automatique lorsqu'un nouveau message est reçu
|
||||
Available,disponible
|
||||
Available Qty at Warehouse,Qté disponible à l'entrepôt
|
||||
Available Stock for Packing Items,Disponible en stock pour l'emballage Articles
|
||||
"Available in BOM, Delivery Note, Purchase Invoice, Production Order, Purchase Order, Purchase Receipt, Sales Invoice, Sales Order, Stock Entry, Timesheet","Disponible en nomenclature , bon de livraison , facture d'achat , ordre de production, bon de commande , bon de réception , la facture de vente , Sales Order , Stock entrée , des feuilles de temps"
|
||||
Average Age,moyen âge
|
||||
Average Commission Rate,Taux moyen Commission
|
||||
Average Discount,D'actualisation moyen
|
||||
Awesome Products,"Pour suivre le nom de la marque dans la note qui suit documents de livraison , Opportunité , Demande de Matériel , article , bon de commande, bon d'achat, l'acheteur réception , offre , facture de vente , ventes BOM , Sales Order , No de série"
|
||||
Awesome Services,Restrictions d'autorisation de l'utilisateur
|
||||
BOM Detail No,Détail BOM Non
|
||||
Average Age,âge moyen
|
||||
Average Commission Rate,Taux moyen de la commission
|
||||
Average Discount,Remise moyenne
|
||||
Awesome Products,Produits impressionnants
|
||||
Awesome Services,Services impressionnants
|
||||
BOM Detail No,Numéro du détail BOM
|
||||
BOM Explosion Item,Article éclatement de la nomenclature
|
||||
BOM Item,Article BOM
|
||||
BOM No,Aucune nomenclature
|
||||
BOM No,Numéro BOM
|
||||
BOM No. for a Finished Good Item,N ° nomenclature pour un produit fini Bonne
|
||||
BOM Operation,Opération BOM
|
||||
BOM Operations,Opérations de nomenclature
|
||||
@@ -314,50 +315,50 @@ BOM {0} for Item {1} in row {2} is inactive or not submitted,Dépenses de voyage
|
||||
BOM {0} is not active or not submitted,Eléments requis
|
||||
BOM {0} is not submitted or inactive BOM for Item {1},BOM {0} n'est pas soumis ou inactif nomenclature pour objet {1}
|
||||
Backup Manager,Gestionnaire de sauvegarde
|
||||
Backup Right Now,Sauvegarde Right Now
|
||||
Backup Right Now,Sauvegarder immédiatement
|
||||
Backups will be uploaded to,Les sauvegardes seront téléchargées sur
|
||||
Balance Qty,solde Quantité
|
||||
Balance Sheet,Fournisseur Type maître .
|
||||
Balance Value,Valeur de balance
|
||||
Balance for Account {0} must always be {1},Point {0} avec Serial Non {1} est déjà installé
|
||||
Balance must be,avec des grands livres
|
||||
"Balances of Accounts of type ""Bank"" or ""Cash""",Date de vieillissement est obligatoire pour l'ouverture d'entrée
|
||||
Balance Qty,Qté soldée
|
||||
Balance Sheet,Bilan
|
||||
Balance Value,Valeur du solde
|
||||
Balance for Account {0} must always be {1},Solde pour le compte {0} doit toujours être {1}
|
||||
Balance must be,Solde doit être
|
||||
"Balances of Accounts of type ""Bank"" or ""Cash""","Solde du compte de type ""Banque"" ou ""Espèces"""
|
||||
Bank,Banque
|
||||
Bank / Cash Account,Banque / Compte de trésorerie
|
||||
Bank A/C No.,Bank A / C No.
|
||||
Bank / Cash Account,Compte en Banque / trésorerie
|
||||
Bank A/C No.,No. de compte bancaire
|
||||
Bank Account,Compte bancaire
|
||||
Bank Account No.,N ° de compte bancaire
|
||||
Bank Account No.,No. de compte bancaire
|
||||
Bank Accounts,Comptes bancaires
|
||||
Bank Clearance Summary,Banque Résumé de dégagement
|
||||
Bank Clearance Summary,Résumé de l'approbation de la banque
|
||||
Bank Draft,Projet de la Banque
|
||||
Bank Name,Nom de la banque
|
||||
Bank Overdraft Account,Inspection de la qualité requise pour objet {0}
|
||||
Bank Overdraft Account,Compte du découvert bancaire
|
||||
Bank Reconciliation,Rapprochement bancaire
|
||||
Bank Reconciliation Detail,Détail de rapprochement bancaire
|
||||
Bank Reconciliation Statement,État de rapprochement bancaire
|
||||
Bank Voucher,Bon Banque
|
||||
Bank/Cash Balance,Banque / Balance trésorerie
|
||||
Banking,bancaire
|
||||
Bank Reconciliation Detail,Détail du rapprochement bancaire
|
||||
Bank Reconciliation Statement,Énoncé de rapprochement bancaire
|
||||
Bank Voucher,Coupon de la banque
|
||||
Bank/Cash Balance,Solde de la banque / trésorerie
|
||||
Banking,Bancaire
|
||||
Barcode,Barcode
|
||||
Barcode {0} already used in Item {1},Lettre d'information a déjà été envoyé
|
||||
Barcode {0} already used in Item {1},Le code barre {0} est déjà utilisé dans l'article {1}
|
||||
Based On,Basé sur
|
||||
Basic,de base
|
||||
Basic Info,Informations de base
|
||||
Basic Information,Renseignements de base
|
||||
Basic Rate,Taux de base
|
||||
Basic Rate (Company Currency),Taux de base (Société Monnaie)
|
||||
Basic Rate (Company Currency),Taux de base (Monnaie de la Société )
|
||||
Batch,Lot
|
||||
Batch (lot) of an Item.,Batch (lot) d'un élément.
|
||||
Batch Finished Date,Date de lot fini
|
||||
Batch ID,ID du lot
|
||||
Batch No,Aucun lot
|
||||
Batch Started Date,Date de démarrage du lot
|
||||
Batch (lot) of an Item.,Lot d'une article.
|
||||
Batch Finished Date,La date finie d'un lot
|
||||
Batch ID,Identifiant du lot
|
||||
Batch No,Numéro du lot
|
||||
Batch Started Date,Date de début du lot
|
||||
Batch Time Logs for billing.,Temps de lots des journaux pour la facturation.
|
||||
Batch-Wise Balance History,Discontinu Histoire de la balance
|
||||
Batched for Billing,Par lots pour la facturation
|
||||
Better Prospects,De meilleures perspectives
|
||||
Bill Date,Bill Date
|
||||
Bill No,Le projet de loi no
|
||||
Bill Date,Date de la facture
|
||||
Bill No,Numéro de la facture
|
||||
Bill No {0} already booked in Purchase Invoice {1},Centre de coûts de transactions existants ne peut pas être converti en livre
|
||||
Bill of Material,De la valeur doit être inférieure à la valeur à la ligne {0}
|
||||
Bill of Material to be considered for manufacturing,Bill of Material être considéré pour la fabrication
|
||||
@@ -386,22 +387,22 @@ Both Warehouse must belong to same Company,Les deux Entrepôt doit appartenir à
|
||||
Box,boîte
|
||||
Branch,Branche
|
||||
Brand,Marque
|
||||
Brand Name,Nom de marque
|
||||
Brand Name,La marque
|
||||
Brand master.,Marque maître.
|
||||
Brands,Marques
|
||||
Breakdown,Panne
|
||||
Broadcasting,radiodiffusion
|
||||
Broadcasting,Diffusion
|
||||
Brokerage,courtage
|
||||
Budget,Budget
|
||||
Budget Allocated,Budget alloué
|
||||
Budget Detail,Détail du budget
|
||||
Budget Details,Détails du budget
|
||||
Budget Distribution,Répartition du budget
|
||||
Budget Distribution Detail,Détail Répartition du budget
|
||||
Budget Distribution Detail,Détail de la répartition du budget
|
||||
Budget Distribution Details,Détails de la répartition du budget
|
||||
Budget Variance Report,Rapport sur les écarts de budget
|
||||
Budget Variance Report,Rapport sur les écarts du budget
|
||||
Budget cannot be set for Group Cost Centers,Imprimer et stationnaire
|
||||
Build Report,construire Rapport
|
||||
Build Report,Créer un rapport
|
||||
Bundle items at time of sale.,Regrouper des envois au moment de la vente.
|
||||
Business Development Manager,Directeur du développement des affaires
|
||||
Buying,Achat
|
||||
@@ -501,7 +502,7 @@ Cheque Date,Date de chèques
|
||||
Cheque Number,Numéro de chèque
|
||||
Child account exists for this account. You can not delete this account.,Les matières premières ne peut pas être le même que l'article principal
|
||||
City,Ville
|
||||
City/Town,Ville /
|
||||
City/Town,Ville
|
||||
Claim Amount,Montant réclamé
|
||||
Claims for company expense.,Les réclamations pour frais de la société.
|
||||
Class / Percentage,Classe / Pourcentage
|
||||
@@ -561,11 +562,11 @@ Complete,Compléter
|
||||
Complete Setup,congé de maladie
|
||||
Completed,Terminé
|
||||
Completed Production Orders,Terminé les ordres de fabrication
|
||||
Completed Qty,Complété Quantité
|
||||
Completed Qty,Quantité complétée
|
||||
Completion Date,Date d'achèvement
|
||||
Completion Status,L'état d'achèvement
|
||||
Computer,ordinateur
|
||||
Computers,Informatique
|
||||
Computers,Ordinateurs
|
||||
Confirmation Date,date de confirmation
|
||||
Confirmed orders from Customers.,Confirmé commandes provenant de clients.
|
||||
Consider Tax or Charge for,Prenons l'impôt ou charge pour
|
||||
@@ -715,7 +716,7 @@ Customize the introductory text that goes as a part of that email. Each transact
|
||||
DN Detail,Détail DN
|
||||
Daily,Quotidien
|
||||
Daily Time Log Summary,Daily Time Sommaire du journal
|
||||
Database Folder ID,Database ID du dossier
|
||||
Database Folder ID,Identifiant du dossier de la base de données
|
||||
Database of potential customers.,Base de données de clients potentiels.
|
||||
Date,Date
|
||||
Date Format,Format de date
|
||||
@@ -745,7 +746,7 @@ Deductions,Déductions
|
||||
Default,Par défaut
|
||||
Default Account,Compte par défaut
|
||||
Default Address Template cannot be deleted,Adresse par défaut modèle ne peut pas être supprimé
|
||||
Default Amount,Montant en défaut
|
||||
Default Amount,Montant par défaut
|
||||
Default BOM,Nomenclature par défaut
|
||||
Default Bank / Cash account will be automatically updated in POS Invoice when this mode is selected.,Par défaut Banque / argent compte sera automatiquement mis à jour dans la facture POS lorsque ce mode est sélectionné.
|
||||
Default Bank Account,Compte bancaire par défaut
|
||||
@@ -779,18 +780,18 @@ Default settings for selling transactions.,principal
|
||||
Default settings for stock transactions.,minute
|
||||
Defense,défense
|
||||
"Define Budget for this Cost Center. To set budget action, see <a href=""#!List/Company"">Company Master</a>","Définir le budget pour ce centre de coûts. Pour définir l'action budgétaire, voir <a href=""#!List/Company"">Maître Société</a>"
|
||||
Del,Eff
|
||||
Delete,Effacer
|
||||
Del,Suppr
|
||||
Delete,Supprimer
|
||||
Delete {0} {1}?,Supprimer {0} {1} ?
|
||||
Delivered,Livré
|
||||
Delivered Items To Be Billed,Les articles livrés être facturé
|
||||
Delivered Items To Be Billed,Les items livrés à être facturés
|
||||
Delivered Qty,Qté livrée
|
||||
Delivered Serial No {0} cannot be deleted,médical
|
||||
Delivery Date,Date de livraison
|
||||
Delivery Details,Détails de la livraison
|
||||
Delivery Document No,Pas de livraison de documents
|
||||
Delivery Document Type,Type de document de livraison
|
||||
Delivery Note,Remarque livraison
|
||||
Delivery Note,Bon de livraison
|
||||
Delivery Note Item,Point de Livraison
|
||||
Delivery Note Items,Articles bordereau de livraison
|
||||
Delivery Note Message,Note Message de livraison
|
||||
@@ -800,9 +801,9 @@ Delivery Note Trends,Bordereau de livraison Tendances
|
||||
Delivery Note {0} is not submitted,Livraison Remarque {0} n'est pas soumis
|
||||
Delivery Note {0} must not be submitted,Pas de clients ou fournisseurs Comptes trouvé
|
||||
Delivery Notes {0} must be cancelled before cancelling this Sales Order,Quantité en ligne {0} ( {1} ) doit être la même que la quantité fabriquée {2}
|
||||
Delivery Status,Delivery Status
|
||||
Delivery Time,Délai de livraison
|
||||
Delivery To,Pour livraison
|
||||
Delivery Status,Statut de la livraison
|
||||
Delivery Time,L'heure de la livraison
|
||||
Delivery To,Livrer à
|
||||
Department,Département
|
||||
Department Stores,Grands Magasins
|
||||
Depends on LWP,Dépend de LWP
|
||||
@@ -822,8 +823,8 @@ Direct Income,Choisissez votre langue
|
||||
Disable,"Groupe ajoutée, rafraîchissant ..."
|
||||
Disable Rounded Total,Désactiver totale arrondie
|
||||
Disabled,Handicapé
|
||||
Discount %,% De réduction
|
||||
Discount %,% De réduction
|
||||
Discount %,% Remise
|
||||
Discount %,% Remise
|
||||
Discount (%),Remise (%)
|
||||
Discount Amount,S'il vous plaît tirer des articles de livraison Note
|
||||
"Discount Fields will be available in Purchase Order, Purchase Receipt, Purchase Invoice","Les champs d'actualisation sera disponible en commande, reçu d'achat, facture d'achat"
|
||||
@@ -1464,8 +1465,8 @@ Journal Voucher {0} does not have account {1} or already matched,Journal Bon {0}
|
||||
Journal Vouchers {0} are un-linked,Date de livraison prévue ne peut pas être avant ventes Date de commande
|
||||
Keep a track of communication related to this enquiry which will help for future reference.,Gardez une trace de la communication liée à cette enquête qui aidera pour référence future.
|
||||
Keep it web friendly 900px (w) by 100px (h),Gardez web 900px amical ( w) par 100px ( h )
|
||||
Key Performance Area,Zone de performance clé
|
||||
Key Responsibility Area,Secteur de responsabilité clé
|
||||
Key Performance Area,Section de performance clé
|
||||
Key Responsibility Area,Section à responsabilité importante
|
||||
Kg,kg
|
||||
LR Date,LR Date
|
||||
LR No,LR Non
|
||||
@@ -1723,9 +1724,9 @@ Net Weight UOM,Emballage Poids Net
|
||||
Net Weight of each Item,Poids net de chaque article
|
||||
Net pay cannot be negative,Landed Cost correctement mis à jour
|
||||
Never,Jamais
|
||||
New ,
|
||||
New ,Nouveau
|
||||
New Account,nouveau compte
|
||||
New Account Name,Nouveau compte Nom
|
||||
New Account Name,Nom du nouveau compte
|
||||
New BOM,Nouvelle nomenclature
|
||||
New Communications,Communications Nouveau-
|
||||
New Company,nouvelle entreprise
|
||||
@@ -1761,11 +1762,11 @@ Newspaper Publishers,Éditeurs de journaux
|
||||
Next,Nombre purchse de commande requis pour objet {0}
|
||||
Next Contact By,Suivant Par
|
||||
Next Contact Date,Date Contact Suivant
|
||||
Next Date,Date d'
|
||||
Next Date,Date suivante
|
||||
Next email will be sent on:,Email sera envoyé le:
|
||||
No,Aucun
|
||||
No,Non
|
||||
No Customer Accounts found.,Aucun client ne représente trouvés.
|
||||
No Customer or Supplier Accounts found,Encaisse
|
||||
No Customer or Supplier Accounts found,Aucun compte client ou fournisseur trouvé
|
||||
No Expense Approvers. Please assign 'Expense Approver' Role to atleast one user,Compte {0} est inactif
|
||||
No Item with Barcode {0},Bon de commande {0} ' arrêté '
|
||||
No Item with Serial No {0},non autorisé
|
||||
@@ -1775,13 +1776,13 @@ No Permission,Aucune autorisation
|
||||
No Production Orders created,Section de base
|
||||
No Supplier Accounts found. Supplier Accounts are identified based on 'Master Type' value in account record.,Aucun fournisseur ne représente trouvés. Comptes fournisseurs sont identifiés sur la base de la valeur «Maître Type ' dans le compte rendu.
|
||||
No accounting entries for the following warehouses,Pas d'entrées comptables pour les entrepôts suivants
|
||||
No addresses created,Aucune adresse créés
|
||||
No addresses created,Aucune adresse créée
|
||||
No contacts created,Pas de contacts créés
|
||||
No default Address Template found. Please create a new one from Setup > Printing and Branding > Address Template.,Aucune valeur par défaut Adresse modèle trouvé. S'il vous plaît créer un nouveau à partir de Configuration> Presse et Branding> Adresse modèle.
|
||||
No default BOM exists for Item {0},services impressionnants
|
||||
No description given,Le jour (s ) sur lequel vous postulez pour congé sont les vacances . Vous n'avez pas besoin de demander l'autorisation .
|
||||
No employee found,Aucun employé
|
||||
No employee found!,Aucun employé !
|
||||
No employee found,Aucun employé trouvé
|
||||
No employee found!,Aucun employé trouvé!
|
||||
No of Requested SMS,Pas de SMS demandés
|
||||
No of Sent SMS,Pas de SMS envoyés
|
||||
No of Visits,Pas de visites
|
||||
@@ -1816,14 +1817,14 @@ Note: This Cost Center is a Group. Cannot make accounting entries against groups
|
||||
Note: {0},Compte avec des nœuds enfants ne peut pas être converti en livre
|
||||
Notes,Remarques
|
||||
Notes:,notes:
|
||||
Nothing to request,Rien à demander
|
||||
Nothing to request,Pas de requête à demander
|
||||
Notice (days),Avis ( jours )
|
||||
Notification Control,Contrôle de notification
|
||||
Notification Email Address,Adresse e-mail de notification
|
||||
Notify by Email on creation of automatic Material Request,Notification par courriel lors de la création de la demande de matériel automatique
|
||||
Number Format,Format numérique
|
||||
Offer Date,offre date
|
||||
Office,Fonction
|
||||
Offer Date,Date de l'offre
|
||||
Office,Bureau
|
||||
Office Equipments,Équipement de bureau
|
||||
Office Maintenance Expenses,Date d'adhésion doit être supérieure à Date de naissance
|
||||
Office Rent,POS Global Setting {0} déjà créé pour la compagnie {1}
|
||||
@@ -1970,7 +1971,7 @@ Payments made during the digest period,Les paiements effectués au cours de la p
|
||||
Payments received during the digest period,Les paiements reçus au cours de la période digest
|
||||
Payroll Settings,Paramètres de la paie
|
||||
Pending,En attendant
|
||||
Pending Amount,Montant attente
|
||||
Pending Amount,Montant en attente
|
||||
Pending Items {0} updated,Machines et installations
|
||||
Pending Review,Attente d'examen
|
||||
Pending SO Items For Purchase Request,"Articles en attente Donc, pour demande d'achat"
|
||||
@@ -2097,11 +2098,11 @@ Please set {0},S'il vous plaît mettre {0}
|
||||
Please setup Employee Naming System in Human Resource > HR Settings,S'il vous plaît configuration Naming System employés en ressources humaines> Paramètres RH
|
||||
Please setup numbering series for Attendance via Setup > Numbering Series,S'il vous plaît configuration série de numérotation à la fréquentation via Configuration> Série de numérotation
|
||||
Please setup your chart of accounts before you start Accounting Entries,S'il vous plaît configurer votre plan de comptes avant de commencer Écritures comptables
|
||||
Please specify,S'il vous plaît spécifier
|
||||
Please specify,Veuillez spécifier
|
||||
Please specify Company,S'il vous plaît préciser Company
|
||||
Please specify Company to proceed,Veuillez indiquer Société de procéder
|
||||
Please specify Default Currency in Company Master and Global Defaults,N ° de série {0} n'existe pas
|
||||
Please specify a,S'il vous plaît spécifier une
|
||||
Please specify a,Veuillez spécifier un
|
||||
Please specify a valid 'From Case No.',S'il vous plaît indiquer une valide »De Affaire n '
|
||||
Please specify a valid Row ID for {0} in row {1},Il s'agit d'un site d'exemple généré automatiquement à partir de ERPNext
|
||||
Please specify either Quantity or Valuation Rate or both,S'il vous plaît spécifier Quantité ou l'évaluation des taux ou à la fois
|
||||
@@ -2205,7 +2206,7 @@ Publishing,édition
|
||||
Pull sales orders (pending to deliver) based on the above criteria,Tirez les ordres de vente (en attendant de livrer) sur la base des critères ci-dessus
|
||||
Purchase,Acheter
|
||||
Purchase / Manufacture Details,Achat / Fabrication Détails
|
||||
Purchase Analytics,Achat Analytics
|
||||
Purchase Analytics,Les analyses des achats
|
||||
Purchase Common,Achat commune
|
||||
Purchase Details,Conditions de souscription
|
||||
Purchase Discounts,Rabais sur l'achat
|
||||
@@ -2256,7 +2257,7 @@ Qty To Manufacture,Quantité à fabriquer
|
||||
Qty as per Stock UOM,Qté en stock pour Emballage
|
||||
Qty to Deliver,Quantité à livrer
|
||||
Qty to Order,Quantité à commander
|
||||
Qty to Receive,Quantité pour recevoir
|
||||
Qty to Receive,Quantité à recevoir
|
||||
Qty to Transfer,Quantité de Transfert
|
||||
Qualification,Qualification
|
||||
Quality,Qualité
|
||||
@@ -2278,11 +2279,11 @@ Quantity required for Item {0} in row {1},Quantité requise pour objet {0} à la
|
||||
Quarter,Trimestre
|
||||
Quarterly,Trimestriel
|
||||
Quick Help,Aide rapide
|
||||
Quotation,Citation
|
||||
Quotation,Devis
|
||||
Quotation Item,Article devis
|
||||
Quotation Items,Articles de devis
|
||||
Quotation Lost Reason,Devis perdu la raison
|
||||
Quotation Message,Devis message
|
||||
Quotation Message,Message du devis
|
||||
Quotation To,Devis Pour
|
||||
Quotation Trends,Devis Tendances
|
||||
Quotation {0} is cancelled,Devis {0} est annulée
|
||||
@@ -2296,8 +2297,8 @@ Random,Aléatoire
|
||||
Range,Gamme
|
||||
Rate,Taux
|
||||
Rate ,Taux
|
||||
Rate (%),S'il vous plaît entrer la date soulager .
|
||||
Rate (Company Currency),Taux (Société Monnaie)
|
||||
Rate (%),Taux (%)
|
||||
Rate (Company Currency),Taux (Monnaie de la société)
|
||||
Rate Of Materials Based On,Taux de matériaux à base
|
||||
Rate and Amount,Taux et le montant
|
||||
Rate at which Customer Currency is converted to customer's base currency,Vitesse à laquelle la devise du client est converti en devise de base du client
|
||||
@@ -2318,15 +2319,15 @@ Re-order Level,Re-order niveau
|
||||
Re-order Qty,Re-order Quantité
|
||||
Read,Lire
|
||||
Reading 1,Lecture 1
|
||||
Reading 10,Lecture le 10
|
||||
Reading 10,Lecture 10
|
||||
Reading 2,Lecture 2
|
||||
Reading 3,Reading 3
|
||||
Reading 4,Reading 4
|
||||
Reading 5,Reading 5
|
||||
Reading 6,Lecture 6
|
||||
Reading 7,Lecture le 7
|
||||
Reading 7,Lecture 7
|
||||
Reading 8,Lecture 8
|
||||
Reading 9,Lectures suggérées 9
|
||||
Reading 9,Lecture 9
|
||||
Real Estate,Immobilier
|
||||
Reason,Raison
|
||||
Reason for Leaving,Raison du départ
|
||||
@@ -2345,7 +2346,7 @@ Received and Accepted,Reçus et acceptés
|
||||
Receiver List,Liste des récepteurs
|
||||
Receiver List is empty. Please create Receiver List,Soit quantité de cible ou le montant cible est obligatoire .
|
||||
Receiver Parameter,Paramètre récepteur
|
||||
Recipients,Récipiendaires
|
||||
Recipients,Destinataires
|
||||
Reconcile,réconcilier
|
||||
Reconciliation Data,Données de réconciliation
|
||||
Reconciliation HTML,Réconciliation HTML
|
||||
@@ -2383,7 +2384,7 @@ Remarks,Remarques
|
||||
Remarks Custom,Remarques sur commande
|
||||
Rename,rebaptiser
|
||||
Rename Log,Renommez identifiez-vous
|
||||
Rename Tool,Renommer l'outil
|
||||
Rename Tool,Outils de renommage
|
||||
Rent Cost,louer coût
|
||||
Rent per hour,Louer par heure
|
||||
Rented,Loué
|
||||
@@ -2419,8 +2420,8 @@ Reseller,Revendeur
|
||||
Reserved,réservé
|
||||
Reserved Qty,Quantité réservés
|
||||
"Reserved Qty: Quantity ordered for sale, but not delivered.","Réservés Quantité: Quantité de commande pour la vente , mais pas livré ."
|
||||
Reserved Quantity,Quantité réservés
|
||||
Reserved Warehouse,Réservé Entrepôt
|
||||
Reserved Quantity,Quantité réservée
|
||||
Reserved Warehouse,Entrepôt réservé
|
||||
Reserved Warehouse in Sales Order / Finished Goods Warehouse,Entrepôt réservé à des commandes clients / entrepôt de produits finis
|
||||
Reserved Warehouse is missing in Sales Order,Réservé entrepôt est manquant dans l'ordre des ventes
|
||||
Reserved Warehouse required for stock Item {0} in row {1},Centre de coûts par défaut de vente
|
||||
@@ -3158,8 +3159,8 @@ Voucher Type and Date,Type de chèques et date
|
||||
Walk In,Walk In
|
||||
Warehouse,entrepôt
|
||||
Warehouse Contact Info,Entrepôt Info Contact
|
||||
Warehouse Detail,Détail d'entrepôt
|
||||
Warehouse Name,Nom d'entrepôt
|
||||
Warehouse Detail,Détail de l'entrepôt
|
||||
Warehouse Name,Nom de l'entrepôt
|
||||
Warehouse and Reference,Entrepôt et référence
|
||||
Warehouse can not be deleted as stock ledger entry exists for this warehouse.,Descendre : {0}
|
||||
Warehouse can only be changed via Stock Entry / Delivery Note / Purchase Receipt,Entrepôt ne peut être modifié via Stock Entrée / bon de livraison / reçu d'achat
|
||||
@@ -3177,15 +3178,15 @@ Warehouse {0}: Parent account {1} does not bolong to the company {2},Entrepôt {
|
||||
Warehouse-Wise Stock Balance,Warehouse-Wise Stock Solde
|
||||
Warehouse-wise Item Reorder,Warehouse-sage Réorganiser article
|
||||
Warehouses,Entrepôts
|
||||
Warehouses.,applicable
|
||||
Warehouses.,Entrepôts.
|
||||
Warn,Avertir
|
||||
Warning: Leave application contains following block dates,Attention: la demande d'autorisation contient les dates de blocs suivants
|
||||
Warning: Material Requested Qty is less than Minimum Order Qty,Attention: Matériel requis Quantité est inférieure Quantité minimum à commander
|
||||
Warning: Sales Order {0} already exists against same Purchase Order number,S'il vous plaît vérifier ' Est Advance' contre compte {0} si c'est une entrée avance .
|
||||
Warning: System will not check overbilling since amount for Item {0} in {1} is zero,Attention : Le système ne vérifie pas la surfacturation depuis montant pour objet {0} dans {1} est nulle
|
||||
Warranty / AMC Details,Garantie / AMC Détails
|
||||
Warranty / AMC Status,Garantie / AMC Statut
|
||||
Warranty Expiry Date,Date d'expiration de garantie
|
||||
Warranty / AMC Details,Garantie / Détails AMC
|
||||
Warranty / AMC Status,Garantie / Statut AMC
|
||||
Warranty Expiry Date,Date d'expiration de la garantie
|
||||
Warranty Period (Days),Période de garantie (jours)
|
||||
Warranty Period (in days),Période de garantie (en jours)
|
||||
We buy this Item,Nous achetons cet article
|
||||
@@ -3237,7 +3238,7 @@ Write Off Outstanding Amount,Ecrire Off Encours
|
||||
Write Off Voucher,Ecrire Off Bon
|
||||
Wrong Template: Unable to find head row.,Modèle tort: Impossible de trouver la ligne de tête.
|
||||
Year,Année
|
||||
Year Closed,L'année Fermé
|
||||
Year Closed,L'année est fermée
|
||||
Year End Date,Fin de l'exercice Date de
|
||||
Year Name,Nom Année
|
||||
Year Start Date,Date de début Année
|
||||
|
||||
|
@@ -38,30 +38,30 @@ A Customer Group exists with same name please change the Customer name or rename
|
||||
A Customer exists with same name,यह नाम से दूसरा ग्राहक मौजूद हैं
|
||||
A Lead with this email id should exist,इस ईमेल आईडी के साथ एक लीड मौजूद होना चाहिए
|
||||
A Product or Service,उत्पाद या सेवा
|
||||
A Supplier exists with same name,सप्लायर एक ही नाम के साथ मौजूद है
|
||||
A symbol for this currency. For e.g. $,इस मुद्रा के लिए एक प्रतीक है. उदाहरण के लिए $
|
||||
A Supplier exists with same name,यह नाम से दूसरा आपूर्तिकर्ता मौजूद है
|
||||
A symbol for this currency. For e.g. $,इस मुद्रा के लिए एक प्रतीक. उदाहरण के लिए $
|
||||
AMC Expiry Date,एएमसी समाप्ति तिथि
|
||||
Abbr,Abbr
|
||||
Abbreviation cannot have more than 5 characters,संक्षिप्त 5 से अधिक वर्ण नहीं हो सकता
|
||||
Above Value,मूल्य से ऊपर
|
||||
Abbr,संक्षिप्त
|
||||
Abbreviation cannot have more than 5 characters,संक्षिप्त 5 से अधिक वर्ण की नहीं हो सकती
|
||||
Above Value,ऊपर मूल्य
|
||||
Absent,अनुपस्थित
|
||||
Acceptance Criteria,स्वीकृति मानदंड
|
||||
Acceptance Criteria,स्वीकृति मापदंड
|
||||
Accepted,स्वीकार किया
|
||||
Accepted + Rejected Qty must be equal to Received quantity for Item {0},स्वीकृत + अस्वीकृत मात्रा मद के लिए प्राप्त मात्रा के बराबर होना चाहिए {0}
|
||||
Accepted Quantity,स्वीकार किए जाते हैं मात्रा
|
||||
Accepted Warehouse,स्वीकार किए जाते हैं वेअरहाउस
|
||||
Accepted Warehouse,स्वीकार किए जाते हैं गोदाम
|
||||
Account,खाता
|
||||
Account Balance,खाता शेष
|
||||
Account Created: {0},खाता बनाया : {0}
|
||||
Account Balance,खाते की शेष राशि
|
||||
Account Created: {0},खाता बन गया : {0}
|
||||
Account Details,खाता विवरण
|
||||
Account Head,लेखाशीर्ष
|
||||
Account Name,खाते का नाम
|
||||
Account Type,खाता प्रकार
|
||||
"Account balance already in Credit, you are not allowed to set 'Balance Must Be' as 'Debit'","खाता शेष राशि पहले से ही क्रेडिट में, आप सेट करने की अनुमति नहीं है 'डेबिट' के रूप में 'बैलेंस होना चाहिए'"
|
||||
"Account balance already in Debit, you are not allowed to set 'Balance Must Be' as 'Credit'","पहले से ही डेबिट में खाता शेष, आप के रूप में 'क्रेडिट' 'बैलेंस होना चाहिए' स्थापित करने के लिए अनुमति नहीं है"
|
||||
"Account balance already in Credit, you are not allowed to set 'Balance Must Be' as 'Debit'","खाते की शेष राशि पहले से ही क्रेडिट में है, कृपया आप शेष राशि को डेबिट के रूप में ही रखें "
|
||||
"Account balance already in Debit, you are not allowed to set 'Balance Must Be' as 'Credit'","खाते की शेष राशि पहले से ही डेबिट में है, कृपया आप शेष राशि को क्रेडिट के रूप में ही रखें"
|
||||
Account for the warehouse (Perpetual Inventory) will be created under this Account.,गोदाम ( सदा सूची ) के लिए खाते में इस खाते के तहत बनाया जाएगा .
|
||||
Account head {0} created,लेखा शीर्ष {0} बनाया
|
||||
Account must be a balance sheet account,खाता एक वित्तीय स्थिति विवरण खाता होना चाहिए
|
||||
Account head {0} created,लेखाशीर्ष {0} बनाया
|
||||
Account must be a balance sheet account,खाता एक वित्तीय स्थिति विवरण का खाता होना चाहिए
|
||||
Account with child nodes cannot be converted to ledger,बच्चे नोड्स के साथ खाता लेजर को परिवर्तित नहीं किया जा सकता है
|
||||
Account with existing transaction can not be converted to group.,मौजूदा लेन - देन के साथ खाता समूह को नहीं बदला जा सकता .
|
||||
Account with existing transaction can not be deleted,मौजूदा लेन - देन के साथ खाता हटाया नहीं जा सकता
|
||||
|
||||
|
@@ -44,7 +44,7 @@ AMC Expiry Date,AMCの有効期限日
|
||||
Abbr,略称
|
||||
Abbreviation cannot have more than 5 characters,略語は、5つ以上の文字を使用することはできません
|
||||
Above Value,値を上回る
|
||||
Absent,ない
|
||||
Absent,いんない
|
||||
Acceptance Criteria,合否基準
|
||||
Accepted,承認済
|
||||
Accepted + Rejected Qty must be equal to Received quantity for Item {0},一般に認められた+拒否数量、アイテムの受信量と等しくなければなりません{0}
|
||||
|
||||
|
@@ -1559,9 +1559,9 @@ Maintain same rate throughout purchase cycle,รักษาอัตราเ
|
||||
Maintenance,การบำรุงรักษา
|
||||
Maintenance Date,วันที่การบำรุงรักษา
|
||||
Maintenance Details,รายละเอียดการบำรุงรักษา
|
||||
Maintenance Schedule,ตารางการบำรุงรักษา
|
||||
Maintenance Schedule Detail,รายละเอียดตารางการบำรุงรักษา
|
||||
Maintenance Schedule Item,รายการตารางการบำรุงรักษา
|
||||
Maintenance Schedule,กำหนดการซ่อมบำรุง
|
||||
Maintenance Schedule Detail,รายละเอียดกำหนดการซ่อมบำรุง
|
||||
Maintenance Schedule Item,รายการกำหนดการซ่อมบำรุง
|
||||
Maintenance Schedule is not generated for all the items. Please click on 'Generate Schedule',ตาราง การบำรุงรักษา ที่ไม่ได้ สร้างขึ้นสำหรับ รายการทั้งหมด กรุณา คลิกที่ 'สร้าง ตาราง '
|
||||
Maintenance Schedule {0} exists against {0},ตาราง การบำรุงรักษา {0} อยู่ กับ {0}
|
||||
Maintenance Schedule {0} must be cancelled before cancelling this Sales Order,ตาราง การบำรุงรักษา {0} ต้อง ถูกยกเลิก ก่อนที่จะ ยกเลิกการ สั่งซื้อการขาย นี้
|
||||
@@ -1574,7 +1574,7 @@ Maintenance Visit Purpose,วัตถุประสงค์ชมการบ
|
||||
Maintenance Visit {0} must be cancelled before cancelling this Sales Order,การบำรุงรักษา ไปที่ {0} ต้อง ถูกยกเลิก ก่อนที่จะ ยกเลิกการ สั่งซื้อการขาย นี้
|
||||
Maintenance start date can not be before delivery date for Serial No {0},วันที่เริ่มต้น การบำรุงรักษา ไม่สามารถ ก่อนวัน ส่งสำหรับ อนุกรม ไม่มี {0}
|
||||
Major/Optional Subjects,วิชาเอก / เสริม
|
||||
Make ,
|
||||
Make ,สร้าง
|
||||
Make Accounting Entry For Every Stock Movement,ทำให้ รายการ บัญชี สำหรับ ทุก การเคลื่อนไหวของ หุ้น
|
||||
Make Bank Voucher,ทำให้บัตรของธนาคาร
|
||||
Make Credit Note,ให้ เครดิต หมายเหตุ
|
||||
@@ -1587,7 +1587,7 @@ Make Invoice,ทำให้ ใบแจ้งหนี้
|
||||
Make Maint. Schedule,ทำให้ Maint ตารางเวลา
|
||||
Make Maint. Visit,ทำให้ Maint เยือน
|
||||
Make Maintenance Visit,ทำให้ การบำรุงรักษา เยี่ยมชม
|
||||
Make Packing Slip,ให้ บรรจุ สลิป
|
||||
Make Packing Slip,สร้าง รายการบรรจุภัณฑ์
|
||||
Make Payment,ชำระเงิน
|
||||
Make Payment Entry,ทำ รายการ ชำระเงิน
|
||||
Make Purchase Invoice,ให้ ซื้อ ใบแจ้งหนี้
|
||||
@@ -1628,7 +1628,7 @@ Mass Mailing,จดหมายมวล
|
||||
Master Name,ชื่อปริญญาโท
|
||||
Master Name is mandatory if account type is Warehouse,ชื่อ ปริญญาโท มีผลบังคับใช้ ถ้าชนิด บัญชี คลังสินค้า
|
||||
Master Type,ประเภทหลัก
|
||||
Masters,โท
|
||||
Masters,ข้อมูลหลัก
|
||||
Match non-linked Invoices and Payments.,ตรงกับใบแจ้งหนี้ไม่ได้เชื่อมโยงและการชำระเงิน
|
||||
Material Issue,ฉบับวัสดุ
|
||||
Material Receipt,ใบเสร็จรับเงินวัสดุ
|
||||
@@ -2465,7 +2465,7 @@ Row {0}:Start Date must be before End Date,แถว {0}: วันที่ เ
|
||||
Rules for adding shipping costs.,กฎระเบียบ สำหรับการเพิ่ม ค่าใช้จ่ายใน การจัดส่งสินค้า
|
||||
Rules for applying pricing and discount.,กฎระเบียบ สำหรับการใช้ การกำหนดราคาและ ส่วนลด
|
||||
Rules to calculate shipping amount for a sale,กฎระเบียบในการคำนวณปริมาณการขนส่งสินค้าสำหรับการขาย
|
||||
S.O. No.,S.O. เลขที่
|
||||
S.O. No.,เลขที่ใบสั่งขาย
|
||||
SHE Cess on Excise,SHE Cess บนสรรพสามิต
|
||||
SHE Cess on Service Tax,SHE Cess กับภาษีบริการ
|
||||
SHE Cess on TDS,SHE Cess ใน TDS
|
||||
@@ -2550,7 +2550,7 @@ Salutation,ประณม
|
||||
Sample Size,ขนาดของกลุ่มตัวอย่าง
|
||||
Sanctioned Amount,จำนวนตามทำนองคลองธรรม
|
||||
Saturday,วันเสาร์
|
||||
Schedule,กำหนด
|
||||
Schedule,กำหนดการ
|
||||
Schedule Date,กำหนดการ วันที่
|
||||
Schedule Details,รายละเอียดตาราง
|
||||
Scheduled,กำหนด
|
||||
@@ -2564,7 +2564,7 @@ Score Earned,คะแนนที่ได้รับ
|
||||
Score must be less than or equal to 5,คะแนน ต้องน้อยกว่า หรือ เท่ากับ 5
|
||||
Scrap %,เศษ%
|
||||
Seasonality for setting budgets.,ฤดูกาลสำหรับงบประมาณการตั้งค่า
|
||||
Secretary,เลขานุการ
|
||||
Secretary,เลขา
|
||||
Secured Loans,เงินให้กู้ยืม ที่มีหลักประกัน
|
||||
Securities & Commodity Exchanges,หลักทรัพย์และ การแลกเปลี่ยน สินค้าโภคภัณฑ์
|
||||
Securities and Deposits,หลักทรัพย์และ เงินฝาก
|
||||
@@ -2603,14 +2603,14 @@ Select your home country and check the timezone and currency.,เลือกป
|
||||
"Selecting ""Yes"" will allow you to create Bill of Material showing raw material and operational costs incurred to manufacture this item.",เลือก "ใช่" จะช่วยให้คุณสามารถสร้างบิลของวัสดุแสดงวัตถุดิบและต้นทุนการดำเนินงานที่เกิดขึ้นในการผลิตรายการนี้
|
||||
"Selecting ""Yes"" will allow you to make a Production Order for this item.",เลือก "ใช่" จะช่วยให้คุณที่จะทำให้การสั่งซื้อการผลิตสำหรับรายการนี้
|
||||
"Selecting ""Yes"" will give a unique identity to each entity of this item which can be viewed in the Serial No master.",เลือก "Yes" จะให้เอกลักษณ์เฉพาะของแต่ละองค์กรเพื่อรายการนี้ซึ่งสามารถดูได้ในหลักหมายเลขเครื่อง
|
||||
Selling,ขาย
|
||||
Selling Settings,การขายการตั้งค่า
|
||||
Selling,การขาย
|
||||
Selling Settings,ตั้งค่าระบบการขาย
|
||||
"Selling must be checked, if Applicable For is selected as {0}",ขายจะต้องตรวจสอบถ้าใช้สำหรับการถูกเลือกเป็น {0}
|
||||
Send,ส่ง
|
||||
Send Autoreply,ส่ง autoreply
|
||||
Send Email,ส่งอีเมล์
|
||||
Send From,ส่งเริ่มต้นที่
|
||||
Send Notifications To,ส่งการแจ้งเตือนไป
|
||||
Send From,ส่งจาก
|
||||
Send Notifications To,แจ้งเตือนไปให้
|
||||
Send Now,ส่งเดี๋ยวนี้
|
||||
Send SMS,ส่ง SMS
|
||||
Send To,ส่งให้
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user