Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
erp5
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Joshua
erp5
Commits
6ca5cfeb
Commit
6ca5cfeb
authored
Nov 25, 2014
by
Gabriel Monnerat
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
erp5_accounting: Improve AccountingTransaction_roundDebitCredit to fix the rounding issue
parent
f236958e
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
259 additions
and
5 deletions
+259
-5
bt5/erp5_accounting/SkinTemplateItem/portal_skins/erp5_accounting/AccountingTransaction_roundDebitCredit.xml
...rp5_accounting/AccountingTransaction_roundDebitCredit.xml
+70
-5
product/ERP5/tests/testAccounting.py
product/ERP5/tests/testAccounting.py
+123
-0
product/ERP5/tests/testInvoice.py
product/ERP5/tests/testInvoice.py
+66
-0
No files found.
bt5/erp5_accounting/SkinTemplateItem/portal_skins/erp5_accounting/AccountingTransaction_roundDebitCredit.xml
View file @
6ca5cfeb
...
...
@@ -52,9 +52,18 @@
<key>
<string>
_body
</string>
</key>
<value>
<string
encoding=
"cdata"
>
<![CDATA[
""" Rounds debit and credit lines on generated transactions, according to \n
precision of this transaction\'s resource.\n
""" Rounds debit and credit lines on generated transactions, according to\n
precision of this transaction\'s resource. \n
\n
What is expected with this script:\n
\n
- All lines are rounded to the currency precision\n
- Amount on the receivable accounting line match invoice total price\n
- total debit == total credit\n
- In reality we probably also want that amount on vat line match invoice vat\n
amount, but we have ignored this.\n
"""\n
\n
precision = context.getQuantityPrecisionFromResource(context.getResource())\n
resource = context.getResourceValue()\n
\n
...
...
@@ -77,9 +86,65 @@ for line in line_list:\n
if abs(round(total_quantity, precision)) >
2 * resource.getBaseUnitQuantity():\n
return\n
\n
# if the difference is
<
= the base quantity unit, we round the last line.\n
if line is not None:\n
line.setQuantity(round(line.getQuantity() - total_quantity, precision))\n
total_price = round(context.getTotalPrice(), precision)\n
account_type_dict = {}\n
\n
for line in line_list:\n
for account in (line.getSourceValue(portal_type=\'Account\'),\n
line.getDestinationValue(portal_type=\'Account\'),):\n
account_type_dict.setdefault(line, set()).add(\n
account is not None and account.getAccountTypeValue() or None)\n
\n
account_type = context.getPortalObject().portal_categories.account_type\n
receivable_type = account_type.asset.receivable\n
payable_type = account_type.liability.payable\n
abs_total_quantity = abs(round(total_quantity, precision))\n
line_to_adjust = None\n
\n
asset_line = None\n
for line, account_type_list in account_type_dict.iteritems():\n
if receivable_type in account_type_list or payable_type in account_type_list:\n
asset_line = line\n
break\n
\n
if not asset_line:\n
assert total_price == 0.0 and total_quantity == 0.0, \\\n
\'receivable or payable line not found.\'\n
return\n
\n
# If we have a difference between total credit and total debit, one line is \n
# chosen to add or remove this difference. The payable or receivable is chosen \n
# only if this line is not matching with invoice total price, because total price\n
# comes from all invoice lines (quantity * price) and it is what should be payed.\n
# And payable or receivable line is the record in the accounting of what has \n
# to be payed. Then, we must not touch it when it already matches.\n
# If is not a payable or receivable, vat or other line (ie. income) is used.\n
if abs_total_quantity != 0:\n
if round(abs(asset_line.getQuantity()), precision) != round(abs(context.getTotalPrice()), precision):\n
# adjust payable or receivable\n
for line in line_list:\n
if receivable_type in account_type_dict[line] or \\\n
payable_type in account_type_dict[line]:\n
line_to_adjust = line\n
break\n
if line_to_adjust is None:\n
# VAT\n
for line in line_list:\n
if receivable_type.refundable_vat in account_type_dict[line] or \\\n
payable_type.collected_vat in account_type_dict[line]:\n
line_to_adjust = line\n
break\n
if line_to_adjust is None:\n
# adjust anything except payable or receivable\n
for line in line_list:\n
if receivable_type not in account_type_dict[line] and \\\n
payable_type not in account_type_dict[line]:\n
line_to_adjust = line\n
break\n
\n
if line_to_adjust is not None:\n
line_to_adjust.setQuantity(\n
round(line_to_adjust.getQuantity() - total_quantity, precision))\n
]]>
</string>
</value>
...
...
product/ERP5/tests/testAccounting.py
View file @
6ca5cfeb
...
...
@@ -2453,6 +2453,11 @@ class TestAccountingExport(AccountingTestCase):
class
TestTransactions
(
AccountingTestCase
):
"""Test behaviours and utility scripts for Accounting Transactions.
"""
def
getBusinessTemplateList
(
self
):
return
AccountingTestCase
.
getBusinessTemplateList
(
self
)
+
\
(
'erp5_invoicing'
,
'erp5_simplified_invoicing'
)
def
_resetIdGenerator
(
self
):
# clear all existing ids in portal ids
self
.
portal
.
portal_ids
.
clearGenerator
(
all
=
True
)
...
...
@@ -3193,6 +3198,124 @@ class TestTransactions(AccountingTestCase):
for
line
in
invoice
.
contentValues
():
self
.
assertTrue
(
line
.
getGroupingReference
())
def
test_roundDebitCredit_raises_if_big_difference
(
self
):
invoice
=
self
.
_makeOne
(
portal_type
=
'Sale Invoice Transaction'
,
lines
=
(
dict
(
source_value
=
self
.
account_module
.
goods_sales
,
source_debit
=
100.032345
),
dict
(
source_value
=
self
.
account_module
.
receivable
,
source_credit
=
100.000001
)))
precision
=
invoice
.
getQuantityPrecisionFromResource
(
invoice
.
getResource
())
invoice
.
newContent
(
portal_type
=
'Invoice Line'
,
quantity
=
1
,
price
=
100
)
self
.
assertRaises
(
invoice
.
AccountingTransaction_roundDebitCredit
)
def
test_roundDebitCredit_when_payable_is_different_total_price
(
self
):
invoice
=
self
.
_makeOne
(
portal_type
=
'Purchase Invoice Transaction'
,
stop_date
=
DateTime
(),
destination_section_value
=
self
.
section
,
source_section_value
=
self
.
organisation_module
.
supplier
,
lines
=
(
dict
(
source_value
=
self
.
account_module
.
goods_purchase
,
id
=
"expense"
,
destination_debit
=
100.000001
),
dict
(
source_value
=
self
.
account_module
.
payable
,
id
=
"payable"
,
destination_credit
=
100.012345
)))
precision
=
invoice
.
getQuantityPrecisionFromResource
(
invoice
.
getResource
())
invoice
.
newContent
(
portal_type
=
'Invoice Line'
,
quantity
=
1
,
price
=
100
)
line_list
=
invoice
.
getMovementList
(
portal_type
=
invoice
.
getPortalAccountingMovementTypeList
())
self
.
assertNotEqual
(
0.0
,
sum
([
round
(
g
.
getQuantity
(),
precision
)
for
g
in
line_list
]))
invoice
.
AccountingTransaction_roundDebitCredit
()
line_list
=
invoice
.
getMovementList
(
portal_type
=
invoice
.
getPortalAccountingMovementTypeList
())
self
.
assertEqual
(
0.0
,
sum
([
round
(
g
.
getQuantity
(),
precision
)
for
g
in
line_list
]))
self
.
assertEqual
(
100.00
,
invoice
.
payable
.
getDestinationCredit
())
self
.
assertEqual
(
100.00
,
invoice
.
expense
.
getDestinationDebit
())
self
.
assertEqual
([],
invoice
.
checkConsistency
())
def
test_roundDebitCredit_when_payable_is_equal_total_price
(
self
):
invoice
=
self
.
_makeOne
(
portal_type
=
'Purchase Invoice Transaction'
,
stop_date
=
DateTime
(),
destination_section_value
=
self
.
section
,
source_section_value
=
self
.
organisation_module
.
supplier
,
lines
=
(
dict
(
source_value
=
self
.
account_module
.
goods_purchase
,
id
=
"expense"
,
destination_debit
=
100.012345
),
dict
(
source_value
=
self
.
account_module
.
payable
,
id
=
"payable"
,
destination_credit
=
100.000001
)))
precision
=
invoice
.
getQuantityPrecisionFromResource
(
invoice
.
getResource
())
invoice
.
newContent
(
portal_type
=
'Invoice Line'
,
quantity
=
1
,
price
=
100
)
line_list
=
invoice
.
getMovementList
(
portal_type
=
invoice
.
getPortalAccountingMovementTypeList
())
self
.
assertNotEqual
(
0.0
,
sum
([
round
(
g
.
getQuantity
(),
precision
)
for
g
in
line_list
]))
invoice
.
AccountingTransaction_roundDebitCredit
()
line_list
=
invoice
.
getMovementList
(
portal_type
=
invoice
.
getPortalAccountingMovementTypeList
())
self
.
assertEqual
(
0.0
,
sum
([
round
(
g
.
getQuantity
(),
precision
)
for
g
in
line_list
]))
self
.
assertEqual
(
100.00
,
invoice
.
payable
.
getDestinationCredit
())
self
.
assertEqual
(
100.00
,
invoice
.
expense
.
getDestinationDebit
())
self
.
assertEqual
([],
invoice
.
checkConsistency
())
def
test_roundDebitCredit_when_receivable_is_equal_total_price
(
self
):
invoice
=
self
.
_makeOne
(
portal_type
=
'Sale Invoice Transaction'
,
stop_date
=
DateTime
(),
destination_section_value
=
self
.
section
,
source_section_value
=
self
.
section
,
lines
=
(
dict
(
source_value
=
self
.
account_module
.
goods_sales
,
id
=
"income"
,
source_credit
=
100.012345
),
dict
(
source_value
=
self
.
account_module
.
receivable
,
id
=
"receivable"
,
source_debit
=
100.000001
)))
precision
=
invoice
.
getQuantityPrecisionFromResource
(
invoice
.
getResource
())
invoice
.
newContent
(
portal_type
=
'Invoice Line'
,
quantity
=
1
,
price
=
100
)
line_list
=
invoice
.
getMovementList
(
portal_type
=
invoice
.
getPortalAccountingMovementTypeList
())
self
.
assertNotEqual
(
sum
([
round
(
g
.
getQuantity
(),
precision
)
for
g
in
line_list
]),
0.0
)
invoice
.
AccountingTransaction_roundDebitCredit
()
line_list
=
invoice
.
getMovementList
(
portal_type
=
invoice
.
getPortalAccountingMovementTypeList
())
self
.
assertEqual
(
sum
([
round
(
g
.
getQuantity
(),
precision
)
for
g
in
line_list
]),
0.0
)
self
.
assertEqual
(
100.00
,
invoice
.
income
.
getSourceCredit
())
self
.
assertEqual
(
100.00
,
invoice
.
receivable
.
getSourceDebit
())
self
.
assertEqual
([],
invoice
.
checkConsistency
())
def
test_roundDebitCredit_when_receivable_is_different_total_price
(
self
):
invoice
=
self
.
_makeOne
(
portal_type
=
'Sale Invoice Transaction'
,
stop_date
=
DateTime
(),
destination_section_value
=
self
.
section
,
source_section_value
=
self
.
section
,
lines
=
(
dict
(
source_value
=
self
.
account_module
.
goods_sales
,
id
=
"income"
,
source_credit
=
100.000001
),
dict
(
source_value
=
self
.
account_module
.
receivable
,
id
=
"receivable"
,
source_debit
=
100.012345
)))
precision
=
invoice
.
getQuantityPrecisionFromResource
(
invoice
.
getResource
())
invoice
.
newContent
(
portal_type
=
'Invoice Line'
,
quantity
=
1
,
price
=
100
)
line_list
=
invoice
.
getMovementList
(
portal_type
=
invoice
.
getPortalAccountingMovementTypeList
())
self
.
assertNotEqual
(
sum
([
round
(
g
.
getQuantity
(),
precision
)
for
g
in
line_list
]),
0.0
)
invoice
.
AccountingTransaction_roundDebitCredit
()
line_list
=
invoice
.
getMovementList
(
portal_type
=
invoice
.
getPortalAccountingMovementTypeList
())
self
.
assertEqual
(
sum
([
round
(
g
.
getQuantity
(),
precision
)
for
g
in
line_list
]),
0.0
)
self
.
assertEqual
(
100.00
,
invoice
.
income
.
getSourceCredit
())
self
.
assertEqual
(
100.00
,
invoice
.
receivable
.
getSourceDebit
())
self
.
assertEqual
([],
invoice
.
checkConsistency
())
def
test_AccountingTransaction_getTotalDebitCredit
(
self
):
# source view
...
...
product/ERP5/tests/testInvoice.py
View file @
6ca5cfeb
...
...
@@ -2607,6 +2607,72 @@ class TestSaleInvoice(TestSaleInvoiceMixin, TestInvoice, ERP5TypeTestCase):
"""
)
sequence_list
.
play
(
self
,
quiet
=
quiet
)
def
stepCreateCurrency
(
self
,
sequence
):
currency
=
self
.
portal
.
currency_module
.
newContent
(
portal_type
=
"Currency"
,
title
=
"Currency"
,
base_unit_quantity
=
0.01
)
sequence
.
edit
(
currency
=
currency
)
def
stepCheckInvoiceWithBadPrecision
(
self
,
sequence
):
portal
=
self
.
portal
vendor
=
sequence
.
get
(
'vendor'
)
invoice
=
portal
.
accounting_module
.
newContent
(
portal_type
=
"Sale Invoice Transaction"
,
specialise
=
self
.
business_process
,
source_section_value
=
vendor
,
start_date
=
self
.
datetime
,
price_currency_value
=
sequence
.
get
(
'currency'
),
destination_section_value
=
sequence
.
get
(
'client1'
),
source_value
=
vendor
)
resource
=
self
.
portal
.
getDefaultModule
(
self
.
resource_portal_type
).
newContent
(
portal_type
=
self
.
resource_portal_type
,
title
=
'Resource'
,
sale_supply_line_source_account
=
"account_module/sale"
,
product_line
=
'apparel'
)
product_line
=
invoice
.
newContent
(
portal_type
=
"Invoice Line"
,
resource_value
=
resource
,
quantity
=
1
,
price
=
0.014
)
product_line
=
invoice
.
newContent
(
portal_type
=
"Invoice Line"
,
resource_value
=
resource
,
quantity
=
1
,
price
=
0.014
)
self
.
tic
()
invoice
.
plan
()
invoice
.
confirm
()
self
.
tic
()
invoice
.
start
()
self
.
tic
()
movement_list
=
invoice
.
getMovementList
(
portal_type
=
invoice
.
getPortalAccountingMovementTypeList
())
receivable_line
=
[
m
for
m
in
movement_list
\
if
m
.
getSourceValue
().
getAccountType
()
==
\
"asset/receivable"
][
0
]
self
.
assertEquals
(
0.03
,
receivable_line
.
getSourceDebit
())
data
=
invoice
.
Invoice_getODTDataDict
()
precision
=
invoice
.
getQuantityPrecisionFromResource
(
invoice
.
getResource
())
self
.
assertEquals
(
round
(
data
[
'total_price'
],
precision
),
receivable_line
.
getSourceDebit
())
vat_line
=
[
m
for
m
in
movement_list
\
if
m
.
getSourceValue
().
getAccountType
()
==
\
"liability/payable/collected_vat"
][
0
]
self
.
assertEquals
(
0.0
,
vat_line
.
getSourceDebit
())
income_line
=
[
m
for
m
in
movement_list
\
if
m
.
getSourceValue
().
getAccountType
()
==
\
"income"
][
0
]
self
.
assertEquals
(
0.03
,
income_line
.
getSourceCredit
())
def
test_AccountingTransaction_roundDebitCredit
(
self
):
"""
Check that with two invoice lines with total price equal 0.14,
the receivable line will be 0.03 and vat line 0
"""
sequence_list
=
SequenceList
()
sequence_list
.
addSequenceString
(
"""
stepCreateCurrency
stepCreateEntities
stepCheckInvoiceWithBadPrecision
"""
)
sequence_list
.
play
(
self
)
def
test_02_TwoInvoicesFromTwoPackingList
(
self
,
quiet
=
quiet
):
"""
This test was created for the following bug:
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment