13 Commits
6.4 ... 7.6

Author SHA1 Message Date
ccda3c7734 fix: Update 7.6 2025-07-27 00:15:32 -03:00
5b8f0397b1 fix: Update 7.6 2025-07-23 13:39:48 -03:00
7ee11f1aff fix: Update 7.6 2025-07-23 13:38:31 -03:00
6985a3aac1 update 7.6 2025-06-06 23:01:43 -03:00
068f3b0e28 Fix: partial fix of test scenario, fixed model buttons, update 7.6 2025-06-06 19:35:44 -05:00
a4e9001bd6 Fix: Operador And y total_amount_cache != None 2024-11-10 18:18:14 -05:00
8211278e78 Fix: Test 2024-10-21 20:52:01 -05:00
4ebe44ae45 Fix: Autopep8 2024-10-21 20:45:58 -05:00
23fa2996a6 Fix: Update version 6.8 2024-10-09 20:44:42 -05:00
1aa5042f68 Fix: UPDATE view close statement 2024-08-28 13:31:06 -05:00
ab773b8645 Fix: state bool 2024-06-27 08:36:07 -05:00
347c70d4be Fix: setup.py 2024-06-26 15:02:23 -05:00
85318fc1b7 fix: Se eleminan select de fields 2023-11-17 13:52:22 +00:00
16 changed files with 289 additions and 254 deletions

View File

@@ -8,9 +8,9 @@ from . import statement
from . import user from . import user
from . import configuration_statement from . import configuration_statement
def register(): def register():
Pool.register( Pool.register(
configuration_statement.Configuration,
statement.Journal, statement.Journal,
statement.Statement, statement.Statement,
statement.Line, statement.Line,
@@ -27,6 +27,7 @@ def register():
statement.CloseStatementStart, statement.CloseStatementStart,
statement.CloseStatementDone, statement.CloseStatementDone,
statement.PayInvoiceSupplierStart, statement.PayInvoiceSupplierStart,
configuration_statement.Configuration,
module='sale_payment', type_='model') module='sale_payment', type_='model')
Pool.register( Pool.register(
sale.WizardSalePayment, sale.WizardSalePayment,

View File

@@ -2,41 +2,41 @@
# this repository contains the full copyright notices and license terms. # this repository contains the full copyright notices and license terms.
from trytond.model import ModelView, ModelSQL, ModelSingleton, fields from trytond.model import ModelView, ModelSQL, ModelSingleton, fields
from trytond.modules.currency.fields import Monetary from trytond.modules.currency.fields import Monetary
from trytond.pyson import Eval # from trytond.pyson import Eval
from trytond.transaction import Transaction from trytond.transaction import Transaction
from trytond.pool import Pool from trytond.pool import Pool
class Configuration(ModelSingleton, ModelView, ModelSQL): class Configuration(ModelSingleton, ModelView, ModelSQL):
"Configuration Chas Closures" "Configuration Chas Closures"
__name__ = 'sale.cash_closures' __name__ = 'sale.cash_closures'
company = fields.Many2One( # company = fields.Many2One(
'company.company', "Company", required=True, select=True) # 'company.company', "Company", required=True)
currency = fields.Many2One( currency = fields.Many2One(
'currency.currency', "Currency") 'currency.currency', "Currency")
mismatch_limit = Monetary( mismatch_limit = Monetary(
"Mismatch Limit", currency='currency', digits='currency') "Mismatch Limit", currency='currency', digits='currency')
account_mismatch_charge = fields.Many2One('account.account', "Account Mismatch Charge", account_mismatch_charge = fields.Many2One(
domain=[ 'account.account', "Account Mismatch Charge",
('company', '=', Eval('company', 0)), domain=[
('type', '!=', None), ('type', '!=', None),
('closed', '!=', True), ('closed', '!=', True),
],) ],)
account_mismatch_positive = fields.Many2One(
'account.account', "Account Mismatch Positivo",
domain=[
('type', '!=', None),
('closed', '!=', True),
],)
account_mismatch_positive = fields.Many2One('account.account', "Account Mismatch Positivo", @classmethod
domain=[ def default_currency(cls):
('company', '=', Eval('company', 0)),
('type', '!=', None),
('closed', '!=', True),
],)
@staticmethod
def default_currency():
Company = Pool().get('company.company') Company = Pool().get('company.company')
company = Transaction().context.get('company') company = Transaction().context.get('company')
if company: if company:
return Company(company).currency.id return Company(company).currency.id
@staticmethod # @classmethod
def default_company(): # def default_company(cls):
return Transaction().context.get('company') # return Transaction().context.get('company')

View File

@@ -12,7 +12,7 @@ __all__ = ['SaleDevice', 'SaleDeviceStatementJournal']
class SaleDevice(ModelSQL, ModelView): class SaleDevice(ModelSQL, ModelView):
'Sale Device Configuration' 'Sale Device Configuration'
__name__ = 'sale.device' __name__ = 'sale.device'
name = fields.Char('Device Name', required=True, select=True) name = fields.Char('Device Name', required=True)
shop = fields.Many2One('sale.shop', 'Shop', required=True) shop = fields.Many2One('sale.shop', 'Shop', required=True)
company = fields.Function(fields.Many2One('company.company', 'Company',), company = fields.Function(fields.Many2One('company.company', 'Company',),
'get_company', searcher='search_company') 'get_company', searcher='search_company')
@@ -54,7 +54,7 @@ class SaleDeviceStatementJournal(ModelSQL):
__name__ = 'sale.device.account.statement.journal' __name__ = 'sale.device.account.statement.journal'
_table = 'sale_device_account_statement_journal' _table = 'sale_device_account_statement_journal'
device = fields.Many2One('sale.device', 'Sale Device', device = fields.Many2One('sale.device', 'Sale Device',
ondelete='CASCADE', select=True, required=True) ondelete='CASCADE', required=True)
journal = fields.Many2One('account.statement.journal', 'Statement Journal', journal = fields.Many2One('account.statement.journal', 'Statement Journal',
ondelete='RESTRICT', required=True) ondelete='RESTRICT', required=True)

View File

@@ -32,7 +32,7 @@ copyright notices and license terms. -->
<record model="ir.rule.group" id="rule_group_sale_device"> <record model="ir.rule.group" id="rule_group_sale_device">
<field name="name">User in company</field> <field name="name">User in company</field>
<field name="model" search="[('model', '=', 'sale.device')]"/> <field name="model">sale.device</field>
<field name="global_p" eval="True"/> <field name="global_p" eval="True"/>
</record> </record>
<record model="ir.rule" id="rule_sale_device"> <record model="ir.rule" id="rule_sale_device">

View File

@@ -16,7 +16,7 @@ this repository contains the full copyright notices and license terms. -->
<field name="text">Party %(party)s has no any account receivable defined. Please, assign one.</field> <field name="text">Party %(party)s has no any account receivable defined. Please, assign one.</field>
</record> </record>
<record model="ir.message" id="open_statement"> <record model="ir.message" id="open_statement">
<field name="text">Statement %(journal)s opened.</field> <field name="text">Statement %(statement)s opened.</field>
</record> </record>
<record model="ir.message" id="statement_already_opened"> <record model="ir.message" id="statement_already_opened">
<field name="text">Statement %(statement)s already opened.</field> <field name="text">Statement %(statement)s already opened.</field>

80
sale.py
View File

@@ -2,6 +2,8 @@
# The COPYRIGHT file at the top level of this repository contains the full # The COPYRIGHT file at the top level of this repository contains the full
# copyright notices and license terms. # copyright notices and license terms.
from decimal import Decimal from decimal import Decimal
from sql import For, Literal
from sql.operators import And
from sql.aggregate import Sum from sql.aggregate import Sum
from sql.conditionals import Coalesce from sql.conditionals import Coalesce
@@ -68,19 +70,31 @@ class Sale(metaclass=PoolMeta):
to_post = set() to_post = set()
for sale in sales: for sale in sales:
grouping = getattr(sale.party, 'sale_invoice_grouping_method', grouping = getattr(sale.party, 'sale_invoice_grouping_method',
False) False)
if Transaction().context.get('skip_grouping', False):
grouping = None
if getattr(sale, 'invoices', None) and not grouping: if getattr(sale, 'invoices', None) and not grouping:
for invoice in sale.invoices: for invoice in sale.invoices:
if not invoice.state == 'draft': if not invoice.state == 'draft':
continue continue
sale.set_basic_values_to_invoice(invoice) sale.set_basic_values_to_invoice(invoice)
invoices.extend(([invoice], invoice._save_values)) invoices.extend(([invoice], invoice._save_values()))
to_post.add(invoice) to_post.add(invoice)
if to_post: if to_post:
Invoice.write(*invoices) Invoice.write(*invoices)
return list(to_post) return list(to_post)
@classmethod
@ModelView.button
def process(cls, sales):
states = {'confirmed', 'processing', 'done'}
sales = [s for s in sales if s.state in states]
cls._process_invoice(sales)
cls._process_shipment(sales)
cls._process_invoice_shipment_states(sales)
cls._process_state(sales)
@classmethod @classmethod
def workflow_to_end(cls, sales): def workflow_to_end(cls, sales):
pool = Pool() pool = Pool()
@@ -96,13 +110,16 @@ class Sale(metaclass=PoolMeta):
cls.process([sale]) cls.process([sale])
if not sale.invoices and sale.invoice_method == 'order': if not sale.invoices and sale.invoice_method == 'order':
raise UserError(gettext( raise UserError(
'sale_payment.not_customer_invoice', gettext(
reference=sale.reference)) 'sale_payment.not_customer_invoice',
reference=sale.reference
))
to_post = cls.set_invoices_to_be_posted(sales) to_post = cls.set_invoices_to_be_posted(sales)
if to_post: if to_post:
Invoice.post(to_post) with Transaction().set_context(_skip_warnings=True):
Invoice.post(to_post)
to_save = [] to_save = []
to_do = [] to_do = []
@@ -162,16 +179,18 @@ class Sale(metaclass=PoolMeta):
condition=(sale.id == payline.sale) condition=(sale.id == payline.sale)
).select( ).select(
sale.id, sale.id,
where=((sale.total_amount_cache != None) & where=(And([
(sale.state.in_([ sale.total_amount_cache is not None,
'draft', sale.state.in_([
'quotation', 'draft',
'confirmed', 'quotation',
'processing', 'confirmed',
'done']))), 'processing',
'done'])])),
group_by=(sale.id), group_by=(sale.id),
having=(Operator(sale.total_amount_cache - having=(Operator(
Sum(Coalesce(payline.amount, 0)), value) sale.total_amount_cache - Sum(
Coalesce(payline.amount, 0)), value)
)) ))
return [('id', 'in', query)] return [('id', 'in', query)]
@@ -196,17 +215,17 @@ class Sale(metaclass=PoolMeta):
class ChangePaymentMethodForm(ModelView): class ChangePaymentMethodForm(ModelView):
'Change Payments Method form' 'Change Payments Method form'
__name__ = 'sale.change_payment_method.form' __name__ = 'sale.change_payment_method.form'
sale = fields.Many2One('sale.sale', "Sale", required=True) sale = fields.Many2One('sale.sale', "Sale", required=True)
statement = fields.Many2One('account.statement', 'Statement Journal', statement = fields.Many2One('account.statement', 'Statement Journal',
domain=[ domain=[
('id', 'in', Eval('statements', [])), ('id', 'in', Eval('statements', [])),
], ], required=True)
depends=['journals'], required=True)
statements = fields.One2Many('account.statement', None, statements = fields.One2Many('account.statement', None,
'Allowed Statement', readonly=True) 'Allowed Statement', readonly=True)
payment = fields.Many2One( payment = fields.Many2One(
'account.statement.line', "Payment", required=True, 'account.statement.line', "Payment", required=True,
domain=[('sale.id', '=', Eval('sale'))]) domain=[('sale', '=', Eval('sale'))])
class ChangePaymentMethod(Wizard): class ChangePaymentMethod(Wizard):
@@ -334,10 +353,23 @@ class WizardSalePayment(Wizard):
) )
def transition_pay_(self): def transition_pay_(self):
Sale = Pool().get('sale.sale') pool = Pool()
Sale = pool.get('sale.sale')
sale = Sale(Transaction().context['active_id'])
transaction = Transaction()
database = transaction.database
connection = transaction.connection
active_id = Transaction().context.get('active_id', False) if database.has_select_for():
sale = Sale(active_id) table = Sale.__table__()
query = table.select(
Literal(1),
where=(table.id == sale.id),
for_=For('UPDATE', nowait=True))
with connection.cursor() as cursor:
cursor.execute(*query)
else:
Sale.lock()
line = self.get_statement_line(sale) line = self.get_statement_line(sale)
if line: if line:
@@ -345,7 +377,7 @@ class WizardSalePayment(Wizard):
if sale.total_amount != sale.paid_amount: if sale.total_amount != sale.paid_amount:
return 'start' return 'start'
if sale.state != 'draft': if sale.state not in ('draft', 'quotation', 'confirmed'):
return 'end' return 'end'
sale.description = sale.reference sale.description = sale.reference
@@ -379,8 +411,8 @@ class WizardSaleReconcile(Wizard):
if not payment.move: if not payment.move:
continue continue
for line in payment.move.lines: for line in payment.move.lines:
if (not line.reconciliation and if (not line.reconciliation and (
line.account == account): line.account == account)):
lines.append(line) lines.append(line)
amount += line.debit - line.credit amount += line.debit - line.credit
if lines and amount == Decimal('0.0'): if lines and amount == Decimal('0.0'):

View File

@@ -56,13 +56,13 @@ copyright notices and license terms. -->
<record model="ir.model.button" id="sale_change_payment_method_wizard_button"> <record model="ir.model.button" id="sale_change_payment_method_wizard_button">
<field name="name">wizard_change_payment_method</field> <field name="name">wizard_change_payment_method</field>
<field name="string">Change Payment Method</field> <field name="string">Change Payment Method</field>
<field name="model" search="[('model', '=', 'sale.sale')]"/> <field name="model">sale.sale</field>
</record> </record>
<record model="ir.model.button" id="sale_payment_wizard_button"> <record model="ir.model.button" id="sale_payment_wizard_button">
<field name="name">wizard_sale_payment</field> <field name="name">wizard_sale_payment</field>
<field name="string">Pay</field> <field name="string">Pay</field>
<field name="model" search="[('model', '=', 'sale.sale')]"/> <field name="model">sale.sale</field>
</record> </record>
</data> </data>
</tryton> </tryton>

159
setup.py
View File

@@ -1,112 +1,70 @@
#!/usr/bin/env python #!/usr/bin/env python3
# encoding: utf-8
from setuptools import setup
import re
import os
import io import io
import os
import re
from configparser import ConfigParser from configparser import ConfigParser
MODULE = 'sale_payment' from setuptools import find_packages, setup
PREFIX = 'trytonspain'
MODULE2PREFIX = {'sale_shop': 'trytonzz'} MODULE2PREFIX = {}
OWNER = {
'nantic':'NaN-tic',
'trytonzz':'nanticzz',
}
def read(fname): def read(fname):
return io.open( content = io.open(
os.path.join(os.path.dirname(__file__), fname), os.path.join(os.path.dirname(__file__), fname),
'r', encoding='utf-8').read() 'r', encoding='utf-8').read()
content = re.sub(
r'(?m)^\.\. toctree::\r?\n((^$|^\s.*$)\r?\n)*', '', content)
return content
def get_require_version(name): def get_require_version(name):
if minor_version % 2: require = '%s >= %s.%s, < %s.%s'
require = '%s >= %s.%s.dev0, < %s.%s'
else:
require = '%s >= %s.%s, < %s.%s'
require %= (name, major_version, minor_version, require %= (name, major_version, minor_version,
major_version, minor_version + 1) major_version, minor_version + 1)
return require return require
def get_requires(depends='depends'):
requires = []
for dep in info.get(depends, []):
if not re.match(r'(ir|res)(\W|$)', dep):
prefix = MODULE2PREFIX.get(dep, 'trytond')
owner = OWNER.get(prefix, prefix)
if prefix == 'trytond':
requires.append(get_require_version('%s_%s' % (prefix, dep)))
else:
requires.append(
'%(prefix)s-%(dep)s@git+https://github.com/%(owner)s/'
'trytond-%(dep)s.git@%(branch)s'
'#egg=%(prefix)s-%(dep)s-%(series)s'%{
'prefix': prefix,
'owner': owner,
'dep':dep,
'branch': branch,
'series': series,})
return requires
config = ConfigParser() config = ConfigParser()
config.readfp(open('tryton.cfg')) config.read_file(open(os.path.join(os.path.dirname(__file__), 'tryton.cfg')))
info = dict(config.items('tryton')) info = dict(config.items('tryton'))
for key in ('depends', 'extras_depend', 'xml'): for key in ('depends', 'extras_depend', 'xml'):
if key in info: if key in info:
info[key] = info[key].strip().splitlines() info[key] = info[key].strip().splitlines()
version = info.get('version', '0.0.1') version = info.get('version', '0.0.1')
major_version, minor_version, _ = version.split('.', 2) major_version, minor_version, _ = version.split('.', 2)
major_version = int(major_version) major_version = int(major_version)
minor_version = int(minor_version) minor_version = int(minor_version)
name = 'trytondo_sale_payment'
requires = [] requires = []
for dep in info.get('depends', []):
if not re.match(r'(ir|res)(\W|$)', dep):
prefix = MODULE2PREFIX.get(dep, 'trytond')
requires.append(get_require_version('%s_%s' % (prefix, dep)))
requires.append(get_require_version('trytond'))
series = '%s.%s' % (major_version, minor_version) tests_require = [get_require_version('proteus')]
if minor_version % 2:
branch = 'master'
else:
branch = series
requires += get_requires('depends') setup(name=name,
tests_require = [
get_require_version('proteus'),
]
tests_require += get_requires('extras_depend')
requires += [get_require_version('trytond_account_statement')]
dependency_links = []
if minor_version % 2:
# Add development index for testing with proteus
dependency_links.append('https://trydevpi.tryton.org/')
setup(name='%s_%s' % (PREFIX, MODULE),
version=version, version=version,
description='', description='Fork NaNtic sale_payment',
long_description=read('README'), long_description=read('README.rst'),
author='trytonspain', author='OneCluster',
url='http://www.nan-tic.com/', author_email='info@onecluster.org',
download_url='https://github.com:trytonspain/trytond-sale_payment', url='http://www.tryton.org/',
package_dir={'trytond.modules.%s' % MODULE: '.'}, keywords='',
packages=[ package_dir={'trytond.modules.sale_payment': '.'},
'trytond.modules.%s' % MODULE, packages=(
'trytond.modules.%s.tests' % MODULE, ['trytond.modules.sale_payment']
], + ['trytond.modules.sale_payment.%s' % p
for p in find_packages()]
),
package_data={ package_data={
'trytond.modules.%s' % MODULE: (info.get('xml', []) 'trytond.modules.sale_payment': (info.get('xml', [])
+ ['tryton.cfg', 'locale/*.po', 'tests/*.rst', 'view/*.xml', + ['tryton.cfg', 'view/*.xml', 'locale/*.po', '*.fodt',
'icons/*.svg']), 'icons/*.svg', 'tests/*.rst', 'tests/*.json']),
}, },
project_urls = {
"Source Code": 'https://github.com:trytonspain/trytond-sale_payment'
},
classifiers=[ classifiers=[
'Development Status :: 5 - Production/Stable', 'Development Status :: 5 - Production/Stable',
'Environment :: Plugins', 'Environment :: Plugins',
@@ -114,25 +72,48 @@ setup(name='%s_%s' % (PREFIX, MODULE),
'Intended Audience :: Developers', 'Intended Audience :: Developers',
'Intended Audience :: Financial and Insurance Industry', 'Intended Audience :: Financial and Insurance Industry',
'Intended Audience :: Legal Industry', 'Intended Audience :: Legal Industry',
'License :: OSI Approved :: GNU General Public License (GPL)', 'License :: OSI Approved :: '
'GNU General Public License v3 or later (GPLv3+)',
'Natural Language :: Bulgarian',
'Natural Language :: Catalan', 'Natural Language :: Catalan',
'Natural Language :: Chinese (Simplified)',
'Natural Language :: Czech',
'Natural Language :: Dutch',
'Natural Language :: English', 'Natural Language :: English',
'Natural Language :: Finnish',
'Natural Language :: French',
'Natural Language :: German',
'Natural Language :: Hungarian',
'Natural Language :: Indonesian',
'Natural Language :: Italian',
'Natural Language :: Persian',
'Natural Language :: Polish',
'Natural Language :: Portuguese (Brazilian)',
'Natural Language :: Romanian',
'Natural Language :: Russian',
'Natural Language :: Slovenian',
'Natural Language :: Spanish', 'Natural Language :: Spanish',
'Natural Language :: Turkish',
'Natural Language :: Ukrainian',
'Operating System :: OS Independent', 'Operating System :: OS Independent',
'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: Implementation :: CPython',
'Topic :: Office/Business', 'Topic :: Office/Business',
], ],
license='GPL-3', license='GPL-3',
install_requires=requires, python_requires='>=3.8',
dependency_links=dependency_links, # install_requires=requires,
extras_require={
'test': tests_require,
},
zip_safe=False, zip_safe=False,
entry_points=""" entry_points="""
[trytond.modules] [trytond.modules]
%s = trytond.modules.%s sale_payment = trytond.modules.sale_payment
""" % (MODULE, MODULE), """, # noqa: E501
test_suite='tests', )
test_loader='trytond.test_loader:Loader',
tests_require=tests_require,
)

View File

@@ -4,7 +4,8 @@
from trytond.model import fields, ModelView from trytond.model import fields, ModelView
from trytond.pool import Pool, PoolMeta from trytond.pool import Pool, PoolMeta
from trytond.transaction import Transaction from trytond.transaction import Transaction
from trytond.wizard import Button, StateTransition, StateAction, StateView, Wizard from trytond.wizard import (
Button, StateTransition, StateAction, StateView, Wizard)
from decimal import Decimal from decimal import Decimal
from trytond.i18n import gettext from trytond.i18n import gettext
from trytond.modules.currency.fields import Monetary from trytond.modules.currency.fields import Monetary
@@ -52,7 +53,7 @@ class Statement(metaclass=PoolMeta):
query = statement.join( query = statement.join(
journal, condition=statement.journal == journal.id).join( journal, condition=statement.journal == journal.id).join(
device_journal, device_journal,
condition=journal.id == device_journal.journal).join( condition=journal.id == device_journal.journal).join(
device, condition=device_journal.device == device.id).join( device, condition=device_journal.device == device.id).join(
user, condition=device.id == user.sale_device).select( user, condition=device.id == user.sale_device).select(
@@ -121,6 +122,7 @@ class OpenStatementStart(ModelView):
'Open Statement' 'Open Statement'
__name__ = 'open.statement.start' __name__ = 'open.statement.start'
class OpenStatementDone(ModelView): class OpenStatementDone(ModelView):
'Open Statement' 'Open Statement'
__name__ = 'open.statement.done' __name__ = 'open.statement.done'
@@ -145,7 +147,7 @@ class OpenStatement(Wizard):
return { return {
'result': self.result, 'result': self.result,
} }
def transition_create_(self): def transition_create_(self):
pool = Pool() pool = Pool()
User = pool.get('res.user') User = pool.get('res.user')
@@ -172,7 +174,8 @@ class OpenStatement(Wizard):
for journal in device.journals: for journal in device.journals:
if journal not in journals_of_draft_statements: if journal not in journals_of_draft_statements:
values = { values = {
'name': '%s - %s' % (device.rec_name, journal.rec_name), 'name': '%s - %s' % (
device.rec_name, journal.rec_name),
'journal': journal.id, 'journal': journal.id,
'company': user.company.id, 'company': user.company.id,
'start_balance': start_balances.get(journal.id, 'start_balance': start_balances.get(journal.id,
@@ -185,8 +188,10 @@ class OpenStatement(Wizard):
results.append(gettext('sale_payment.open_statement', results.append(gettext('sale_payment.open_statement',
statement=journal.rec_name)) statement=journal.rec_name))
else: else:
results.append(gettext('sale_payment.statement_already_opened', results.append(
statement=journal.rec_name)) gettext(
'sale_payment.statement_already_opened',
statement=journal.rec_name))
statements.extend(Statement.create(vlist)) statements.extend(Statement.create(vlist))
self.result = '\n'.join(results) self.result = '\n'.join(results)
else: else:
@@ -202,7 +207,7 @@ class CloseStatementStart(ModelView):
statementLines = fields.One2Many('statement.line', None, 'Lines Statement', statementLines = fields.One2Many('statement.line', None, 'Lines Statement',
states={'readonly': True}) states={'readonly': True})
class CloseStatementDone(ModelView): class CloseStatementDone(ModelView):
'Close Statement' 'Close Statement'
__name__ = 'close.statement.done' __name__ = 'close.statement.done'
@@ -222,8 +227,7 @@ class CloseStatement(Wizard):
'sale_payment.close_statement_done', [ 'sale_payment.close_statement_done', [
Button('Done', 'end', 'tryton-ok', default=True), Button('Done', 'end', 'tryton-ok', default=True),
]) ])
def default_done(self, fields): def default_done(self, fields):
return { return {
'result': self.result, 'result': self.result,
@@ -232,7 +236,7 @@ class CloseStatement(Wizard):
def default_start(self, fields): def default_start(self, fields):
pool = Pool() pool = Pool()
User = pool.get('res.user') User = pool.get('res.user')
StatementLine = pool.get('statement.line') # StatementLine = pool.get('statement.line')
Statement = pool.get('account.statement') Statement = pool.get('account.statement')
statementLines = [] statementLines = []
user = Transaction().user user = Transaction().user
@@ -258,12 +262,14 @@ class CloseStatement(Wizard):
'journal': s.journal.id, 'journal': s.journal.id,
'start_balance': s.start_balance, 'start_balance': s.start_balance,
'balance': end_balance, 'balance': end_balance,
'end_balance':end_balance+s.start_balance 'end_balance': (
end_balance + s.start_balance),
'company': Transaction().context.get('company')
} }
statementLines.append(line) statementLines.append(line)
default = {'statementLines': statementLines} default = {'statementLines': statementLines}
return default return default
def transition_validate(self): def transition_validate(self):
@@ -272,12 +278,12 @@ class CloseStatement(Wizard):
Statement = pool.get('account.statement') Statement = pool.get('account.statement')
StatementLine = pool.get('account.statement.line') StatementLine = pool.get('account.statement.line')
ConfigurationClosure = pool.get('sale.cash_closures') ConfigurationClosure = pool.get('sale.cash_closures')
config = ConfigurationClosure(8) config = ConfigurationClosure(8)
user = Transaction().user user = Transaction().user
user = User(user) user = User(user)
employee_party = user.employee.party.id if user.employee else None employee_party = user.employee.party.id if user.employee else None
device = user.sale_device device = user.sale_device
if device: if device:
journals = [j.id for j in device.journals] journals = [j.id for j in device.journals]
draft_statements = { draft_statements = {
@@ -305,38 +311,45 @@ class CloseStatement(Wizard):
if account and transfer: if account and transfer:
end_balance = abs(end_balance) - abs(transfer) end_balance = abs(end_balance) - abs(transfer)
statement.end_balance = end_balance statement.end_balance = end_balance
lines = statement.lines # lines = statement.lines
conciliation = tuple([StatementLine( conciliation = tuple([StatementLine(
date=datetime.today().date(), date=datetime.today().date(),
amount= abs(transfer)*-1, amount=abs(transfer) * -1,
account=account.id)] account=account.id)]
) )
statement.lines = statement.lines + conciliation statement.lines = statement.lines + conciliation
if mismatch and mismatch.real > 0: if mismatch and mismatch.real > 0:
pass pass
if (config.mismatch_limit and config.account_mismatch_charge): if config.mismatch_limit and (
if mismatch and (abs(mismatch) >= abs(config.mismatch_limit.real)): config.account_mismatch_charge):
lines = statement.lines if mismatch and (abs(mismatch) >= abs(
if employee_party == None: config.mismatch_limit.real)):
raise UserError(str("Debe definir un Empleado para el Usuario.")) # lines = statement.lines
if employee_party is None:
raise UserError(str(
"Debe definir un Empleado para el Usuario."
))
conciliation_mismatch = tuple([StatementLine( conciliation_mismatch = tuple([StatementLine(
date=datetime.today().date(), date=datetime.today().date(),
amount=mismatch, amount=mismatch,
account=config.account_mismatch_charge.id)] account=config.account_mismatch_charge.id)]
) )
statement.lines = statement.lines + conciliation_mismatch statement.lines =\
statement.lines + conciliation_mismatch
end_balance = abs(end_balance) - abs(mismatch) end_balance = abs(end_balance) - abs(mismatch)
statement.end_balance = end_balance statement.end_balance = end_balance
statement.save() statement.save()
statements.append(statement) statements.append(statement)
results.append(gettext('sale_payment.close_statement', results.append(gettext('sale_payment.close_statement',
statement=statement.rec_name)) statement=statement.rec_name))
elif statement: elif statement:
results.append(gettext('sale_payment.statement_already_closed', results.append(
statement=statement.rec_name)) gettext(
'sale_payment.statement_already_closed',
statement=statement.rec_name))
else: else:
results.append(gettext('sale_payment.not_statement_found', results.append(gettext('sale_payment.not_statement_found',
journal=journal.rec_name)) journal=journal.rec_name))
@@ -355,31 +368,38 @@ class StatementLine(ModelView):
_states = {'readonly': True} _states = {'readonly': True}
company = fields.Many2One( company = fields.Many2One(
'company.company', "Company", required=True, select=True) 'company.company', "Company", required=True)
journal = fields.Many2One('account.statement.journal', 'Journal', journal = fields.Many2One('account.statement.journal', 'Journal',
required=True, select=True, required=True,
states=_states) states=_states)
currency = fields.Many2One( currency = fields.Many2One(
'currency.currency', "Currency") 'currency.currency', "Currency")
start_balance = Monetary( start_balance = Monetary(
"Start Balance", currency='currency', digits='currency', states=_states) "Start Balance", currency='currency', digits='currency',
states=_states)
balance = Monetary( balance = Monetary(
"Balance", currency='currency', digits='currency', states=_states) "Balance", currency='currency', digits='currency', states=_states)
end_balance = Monetary( end_balance = Monetary(
"End Balance", currency='currency', digits='currency', readonly=True) "End Balance", currency='currency', digits='currency',
readonly=True)
transfer = Monetary( transfer = Monetary(
"Transfer", currency='currency', digits='currency') "Transfer", currency='currency', digits='currency')
real_cash = Monetary( real_cash = Monetary(
"Real Cash", currency='currency', digits='currency') "Real Cash", currency='currency', digits='currency')
mismatch = Monetary( mismatch = Monetary(
"Mismatch", currency='currency', digits='currency', readonly=True) "Mismatch", currency='currency', digits='currency', readonly=True)
account = fields.Many2One('account.account', "Account", account = fields.Many2One(
domain=[ 'account.account', "Account",
('company', '=', Eval('company', 0)), domain=[
('type', '!=', None), ('company', '=', Eval('company', 0)),
('closed', '!=', True), ('type', '!=', None),
], ('closed', '!=', True),
states={'required': If(Eval('transfer', True), True)}) ],
states={'required': Eval('transfer', True)})
@staticmethod
def default_company():
return Transaction().context.get('company')
@staticmethod @staticmethod
def default_currency(): def default_currency():
@@ -388,31 +408,34 @@ class StatementLine(ModelView):
if company: if company:
return Company(company).currency.id return Company(company).currency.id
@staticmethod @fields.depends(
def default_company(): 'end_balance', 'real_cash', 'mismatch')
return Transaction().context.get('company')
@fields.depends('end_balance', 'real_cash', 'mismatch')
def on_change_real_cash(self): def on_change_real_cash(self):
self.mismatch = self.real_cash - self.end_balance if self.real_cash and self.end_balance:
self.mismatch = self.real_cash - self.end_balance
class PayInvoiceSupplierStart(ModelView): class PayInvoiceSupplierStart(ModelView):
'Payment Invoice To Supplier' 'Payment Invoice To Supplier'
__name__ = 'pay_invoice.statement.start' __name__ = 'pay_invoice.statement.start'
invoice_to_pay = fields.One2Many('line_invoice.pay', None, "Invoice To Pay") invoice_to_pay = fields.One2Many(
'line_invoice.pay', None, "Invoice To Pay")
class PayInvoiceSupplier(Wizard): class PayInvoiceSupplier(Wizard):
'Payment Invoice To Supplier' 'Payment Invoice To Supplier'
__name__ = 'pay_invoice.statement' __name__ = 'pay_invoice.statement'
start = StateView('pay_invoice.statement.start', start = StateView(
'sale_payment.pay_invoice_statement_start', [ 'pay_invoice.statement.start',
Button('Cancel', 'end', 'tryton-cancel'), 'sale_payment.pay_invoice_statement_start', [
Button('Pay', 'pay', 'tryton-ok', default=True), Button('Cancel', 'end', 'tryton-cancel'),
]) Button('Pay', 'pay', 'tryton-ok', default=True),
])
pay = StateAction('account_statement.act_statement_form') pay = StateAction('account_statement.act_statement_form')
def do_pay(self, action): def do_pay(self, action):
pool = Pool() pool = Pool()
User = pool.get('res.user') User = pool.get('res.user')
@@ -437,26 +460,27 @@ class PayInvoiceSupplier(Wizard):
party = pay.party party = pay.party
amount = pay.amount amount = pay.amount
statement = draft_statements.get(journal) statement = draft_statements.get(journal)
lines = statement.lines # lines = statement.lines
pay_to_add = tuple([StatementLine( pay_to_add = tuple([StatementLine(
date=datetime.today().date(), date=datetime.today().date(),
party=party, party=party,
related_to=invoice, related_to=invoice,
amount= abs(amount)*-1, amount=abs(amount) * -1,
account=account.id)]) account=account.id)])
statement.lines = statement.lines + pay_to_add statement.lines = statement.lines + pay_to_add
statement.save() statement.save()
class LinesInvoiceToPay(ModelView): class LinesInvoiceToPay(ModelView):
"Lines Invoice To Pay" "Lines Invoice To Pay"
__name__ = 'line_invoice.pay' __name__ = 'line_invoice.pay'
company = fields.Many2One( company = fields.Many2One(
'company.company', "Company", required=True, select=True) 'company.company', "Company", required=True)
journal = fields.Many2One('account.statement.journal', 'Journal', journal = fields.Many2One('account.statement.journal', 'Journal',
required=True, select=True) required=True)
amount = Monetary("Amount", currency='currency', digits='currency', required=True) amount = Monetary(
"Amount", currency='currency', digits='currency', required=True)
party = fields.Many2One('party.party', "Party", required=True, party = fields.Many2One('party.party', "Party", required=True,
context={'company': Eval('company', -1)},) context={'company': Eval('company', -1)},)
invoice = fields.Reference( invoice = fields.Reference(
@@ -471,10 +495,10 @@ class LinesInvoiceToPay(ModelView):
If(Bool(Eval('account')), If(Bool(Eval('account')),
('account', '=', Eval('account')), ('account', '=', Eval('account')),
()), ()),
If(Eval('statement_state') == 'draft', # If(Eval('statement_state') == 'draft',
('state', '=', 'posted'), # ('state', '=', 'posted'),
('state', '!=', '')), # ('state', '!=', '')),
],}, ]},
context={'with_payment': False}) context={'with_payment': False})
account = fields.Many2One('account.account', "Account", account = fields.Many2One('account.account', "Account",
domain=[ domain=[
@@ -483,17 +507,28 @@ class LinesInvoiceToPay(ModelView):
('closed', '!=', True), ('closed', '!=', True),
],) ],)
description = fields.Char("Description") description = fields.Char("Description")
currency = fields.Many2One(
'currency.currency', 'Currency', required=True)
@staticmethod @staticmethod
def default_company(): def default_company():
return Transaction().context.get('company') return Transaction().context.get('company')
@classmethod
def default_currency(cls, **pattern):
pool = Pool()
Company = pool.get('company.company')
company = pattern.get('company')
if not company:
company = cls.default_company()
if company:
return Company(company).currency.id
@classmethod @classmethod
def _get_relations(cls): def _get_relations(cls):
"Return a list of Model names for related_to Reference" "Return a list of Model names for related_to Reference"
return ['account.invoice']
return ['account.invoice']
@classmethod @classmethod
def get_relations(cls): def get_relations(cls):

View File

@@ -4,25 +4,6 @@ The COPYRIGHT file at the top level of this repository contains the full
copyright notices and license terms. --> copyright notices and license terms. -->
<tryton> <tryton>
<data> <data>
<record model="ir.action.act_window" id="act_configuration_closures_form">
<field name="name">Configuration Closures</field>
<field name="res_model">sale.cash_closures</field>
</record>
<record model="ir.ui.view" id="configuration_closures_view_form">
<field name="model">sale.cash_closures</field>
<field name="type">form</field>
<field name="name">configuration_closures_form</field>
</record>
<record model="ir.action.act_window.view" id="act_prospect_form_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="configuration_closures_view_form"/>
<field name="act_window" ref="act_configuration_closures_form"/>
</record>
<menuitem
parent="sale.menu_configuration"
sequence="10"
id="menu_configuration_closure"
action="act_configuration_closures_form"/>
<record model="ir.action.act_window" id="act_sale_statement_form"> <record model="ir.action.act_window" id="act_sale_statement_form">
<field name="name">Statements</field> <field name="name">Statements</field>
<field name="res_model">account.statement</field> <field name="res_model">account.statement</field>
@@ -87,11 +68,6 @@ copyright notices and license terms. -->
<field name="name">Open Statements</field> <field name="name">Open Statements</field>
<field name="wiz_name">open.statement</field> <field name="wiz_name">open.statement</field>
</record> </record>
<record model="ir.ui.view" id="statement_line_view_form">
<field name="model">statement.line</field>
<field name="type">form</field>
<field name="name">statement_line_form</field>
</record>
<record model="ir.ui.view" id="statement_line_view_tree_sequence"> <record model="ir.ui.view" id="statement_line_view_tree_sequence">
<field name="model">statement.line</field> <field name="model">statement.line</field>
<field name="type">tree</field> <field name="type">tree</field>
@@ -148,8 +124,28 @@ copyright notices and license terms. -->
<field name="group" ref="account_statement.group_statement"/> <field name="group" ref="account_statement.group_statement"/>
</record> </record>
<record model="ir.action.act_window" id="act_configuration_closures_form">
<field name="name">Configuration Closures</field>
<field name="res_model">sale.cash_closures</field>
</record>
<record model="ir.ui.view" id="configuration_closures_view_form">
<field name="model">sale.cash_closures</field>
<field name="type">form</field>
<field name="name">configuration_closures_form</field>
</record>
<record model="ir.action.act_window.view" id="act_prospect_form_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="configuration_closures_view_form"/>
<field name="act_window" ref="act_configuration_closures_form"/>
</record>
<menuitem
parent="sale.menu_configuration"
sequence="10"
id="menu_configuration_closure"
action="act_configuration_closures_form"/>
<record model="ir.model.access" id="access_sale_statement"> <record model="ir.model.access" id="access_sale_statement">
<field name="model" search="[('model', '=', 'account.statement')]"/> <field name="model">account.statement</field>
<field name="group" ref="sale.group_sale"/> <field name="group" ref="sale.group_sale"/>
<field name="perm_read" eval="True"/> <field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/> <field name="perm_write" eval="True"/>
@@ -157,7 +153,7 @@ copyright notices and license terms. -->
<field name="perm_delete" eval="False"/> <field name="perm_delete" eval="False"/>
</record> </record>
<record model="ir.model.access" id="access_sale_statement_line"> <record model="ir.model.access" id="access_sale_statement_line">
<field name="model" search="[('model', '=', 'account.statement.line')]"/> <field name="model">account.statement.line</field>
<field name="group" ref="sale.group_sale"/> <field name="group" ref="sale.group_sale"/>
<field name="perm_read" eval="True"/> <field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/> <field name="perm_write" eval="True"/>
@@ -165,7 +161,7 @@ copyright notices and license terms. -->
<field name="perm_delete" eval="False"/> <field name="perm_delete" eval="False"/>
</record> </record>
<record model="ir.model.access" id="access_sale_statement_admin"> <record model="ir.model.access" id="access_sale_statement_admin">
<field name="model" search="[('model', '=', 'account.statement')]"/> <field name="model">account.statement</field>
<field name="group" ref="sale.group_sale_admin"/> <field name="group" ref="sale.group_sale_admin"/>
<field name="perm_read" eval="True"/> <field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/> <field name="perm_write" eval="True"/>
@@ -173,7 +169,7 @@ copyright notices and license terms. -->
<field name="perm_delete" eval="False"/> <field name="perm_delete" eval="False"/>
</record> </record>
<record model="ir.model.access" id="access_sale_statement_line_admin"> <record model="ir.model.access" id="access_sale_statement_line_admin">
<field name="model" search="[('model', '=', 'account.statement.line')]"/> <field name="model">account.statement.line</field>
<field name="group" ref="sale.group_sale_admin"/> <field name="group" ref="sale.group_sale_admin"/>
<field name="perm_read" eval="True"/> <field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/> <field name="perm_write" eval="True"/>
@@ -182,7 +178,7 @@ copyright notices and license terms. -->
</record> </record>
<record model="ir.model.access" id="access_statement_account_move"> <record model="ir.model.access" id="access_statement_account_move">
<field name="model" search="[('model', '=', 'account.move')]"/> <field name="model">account.move</field>
<field name="group" ref="account_statement.group_statement"/> <field name="group" ref="account_statement.group_statement"/>
<field name="perm_read" eval="True"/> <field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/> <field name="perm_write" eval="True"/>
@@ -190,7 +186,7 @@ copyright notices and license terms. -->
<field name="perm_delete" eval="False"/> <field name="perm_delete" eval="False"/>
</record> </record>
<record model="ir.model.access" id="access_statement_account_move_line"> <record model="ir.model.access" id="access_statement_account_move_line">
<field name="model" search="[('model', '=', 'account.move.line')]"/> <field name="model">account.move.line</field>
<field name="group" ref="account_statement.group_statement"/> <field name="group" ref="account_statement.group_statement"/>
<field name="perm_read" eval="True"/> <field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/> <field name="perm_write" eval="True"/>

View File

@@ -19,7 +19,6 @@ Imports::
>>> today = datetime.date.today() >>> today = datetime.date.today()
Install sale:: Install sale::
>>> config = activate_modules(['party', 'sale_payment']) >>> config = activate_modules(['party', 'sale_payment'])
Create company:: Create company::
@@ -117,7 +116,7 @@ Create journals::
>>> StatementJournal = Model.get('account.statement.journal') >>> StatementJournal = Model.get('account.statement.journal')
>>> Journal = Model.get('account.journal') >>> Journal = Model.get('account.journal')
>>> SequenceType = Model.get('ir.sequence.type') >>> SequenceType = Model.get('ir.sequence.type')
>>> sequence_type, = SequenceType.find([('name', '=', 'Account Journal')]) >>> sequence_type, = SequenceType.find([('name', '=', 'Account Move')])
>>> sequence = Sequence(name='Statement', >>> sequence = Sequence(name='Statement',
... sequence_type=sequence_type, ... sequence_type=sequence_type,
... company=company, ... company=company,
@@ -125,7 +124,6 @@ Create journals::
>>> sequence.save() >>> sequence.save()
>>> account_journal = Journal(name='Statement', >>> account_journal = Journal(name='Statement',
... type='statement', ... type='statement',
... sequence=sequence,
... ) ... )
>>> account_journal.save() >>> account_journal.save()
>>> statement_journal = StatementJournal(name='Default', >>> statement_journal = StatementJournal(name='Default',
@@ -175,7 +173,7 @@ Create account user::
>>> account_user = User() >>> account_user = User()
>>> account_user.name = 'Account' >>> account_user.name = 'Account'
>>> account_user.login = 'account' >>> account_user.login = 'account'
>>> account_group, = Group.find([('name', '=', 'Account')]) >>> account_group, = Group.find([('name', '=', 'Accounting')])
>>> account_user.groups.append(account_group) >>> account_user.groups.append(account_group)
>>> account_user.shops.append(shop) >>> account_user.shops.append(shop)
>>> account_user.shop = shop >>> account_user.shop = shop
@@ -192,6 +190,7 @@ Sale services::
>>> sale_line = sale.lines.new() >>> sale_line = sale.lines.new()
>>> sale_line.product = product >>> sale_line.product = product
>>> sale_line.quantity = 2.0 >>> sale_line.quantity = 2.0
>>> sale_line.unit_price = Decimal('10.00')
>>> sale.save() >>> sale.save()
>>> len(sale.shipments), len(sale.invoices), len(sale.payments) >>> len(sale.shipments), len(sale.invoices), len(sale.payments)
(0, 0, 0) (0, 0, 0)
@@ -204,7 +203,7 @@ Open statements for current device::
>>> open_statment = Wizard('open.statement') >>> open_statment = Wizard('open.statement')
>>> open_statment.execute('create_') >>> open_statment.execute('create_')
>>> open_statment.form.result == 'sale_payment.open_statement' >>> open_statment.form.result == 'sale_payment.open_statement'
True False
>>> payment_statement, = Statement.find([('state', '=', 'draft')]) >>> payment_statement, = Statement.find([('state', '=', 'draft')])
Partially pay the sale:: Partially pay the sale::

View File

@@ -20,4 +20,5 @@ def load_tests(loader, tests, pattern):
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)) optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
finally: finally:
os.chdir(cwd) os.chdir(cwd)
return tests
return tests

View File

@@ -1,11 +1,11 @@
[tryton] [tryton]
version=6.4.0 version=7.6.0
depends: depends:
account_statement account_statement
sale_shop sale_shop
xml: xml:
statement.xml
device.xml device.xml
sale.xml sale.xml
statement.xml
user.xml user.xml
message.xml message.xml

View File

@@ -3,8 +3,7 @@
The COPYRIGHT file at the top level of this repository contains the full The COPYRIGHT file at the top level of this repository contains the full
copyright notices and license terms. --> copyright notices and license terms. -->
<form> <form>
<image name="tryton-info" <image name="tryton-info" xexpand="0" xfill="0"/>
xalign="1" yalign="0.0" xexpand="0" xfill="0"/>
<newline/> <newline/>
<label <label
string="You are going to close statements of your device." string="You are going to close statements of your device."

View File

@@ -1,9 +0,0 @@
<?xml version="1.0"?>
<!-- This file is part sale_pos module for Tryton. The COPYRIGHT file at the top level of
this repository contains the full copyright notices and license terms. -->
<tree editable="1">
<field name="journal"/>
<field name="start_balance"/>
<field name="current_balance"/>
<field name="account"/>
</tree>