media ok + test
This commit is contained in:
parent
0100653643
commit
2933a217e2
13 changed files with 565 additions and 33 deletions
3
.hgignore
Normal file
3
.hgignore
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
syntax: glob
|
||||||
|
__pycache__/*
|
||||||
|
locale/convert_de2en.py
|
34
__init__.py
34
__init__.py
|
@ -4,39 +4,9 @@
|
||||||
# full copyright notices and license terms.
|
# full copyright notices and license terms.
|
||||||
|
|
||||||
from trytond.pool import Pool
|
from trytond.pool import Pool
|
||||||
from .book import Book
|
from .line import Line
|
||||||
from .types import Type
|
|
||||||
from .line import Line, LineContext
|
|
||||||
from .splitline import SplitLine
|
|
||||||
from .wizard_openline import OpenCashBook, OpenCashBookStart, OpenCashBookTree
|
|
||||||
from .wizard_runreport import RunCbReport, RunCbReportStart
|
|
||||||
from .wizard_booking import EnterBookingWizard, EnterBookingStart
|
|
||||||
from .configuration import Configuration, UserConfiguration
|
|
||||||
from .category import Category
|
|
||||||
from .reconciliation import Reconciliation
|
|
||||||
from .cbreport import ReconciliationReport
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
Pool.register(
|
Pool.register(
|
||||||
Configuration,
|
|
||||||
UserConfiguration,
|
|
||||||
Type,
|
|
||||||
Category,
|
|
||||||
Book,
|
|
||||||
LineContext,
|
|
||||||
Line,
|
Line,
|
||||||
SplitLine,
|
module='cashbook_media', type_='model')
|
||||||
Reconciliation,
|
|
||||||
OpenCashBookStart,
|
|
||||||
RunCbReportStart,
|
|
||||||
EnterBookingStart,
|
|
||||||
module='cashbook', type_='model')
|
|
||||||
Pool.register(
|
|
||||||
ReconciliationReport,
|
|
||||||
module='cashbook', type_='report')
|
|
||||||
Pool.register(
|
|
||||||
OpenCashBook,
|
|
||||||
OpenCashBookTree,
|
|
||||||
RunCbReport,
|
|
||||||
EnterBookingWizard,
|
|
||||||
module='cashbook', type_='wizard')
|
|
||||||
|
|
165
line.py
Normal file
165
line.py
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# This file is part of the cashbook-module from m-ds for Tryton.
|
||||||
|
# The COPYRIGHT file at the top level of this repository contains the
|
||||||
|
# full copyright notices and license terms.
|
||||||
|
|
||||||
|
import mimetypes, magic
|
||||||
|
from io import BytesIO
|
||||||
|
from PIL import Image
|
||||||
|
from trytond.model import fields
|
||||||
|
from trytond.pool import Pool, PoolMeta
|
||||||
|
from trytond.config import config
|
||||||
|
from trytond.transaction import Transaction
|
||||||
|
from trytond.exceptions import UserError
|
||||||
|
from trytond.i18n import gettext
|
||||||
|
from trytond.pyson import Eval, Bool
|
||||||
|
from trytond.modules.cashbook.line import STATES, DEPENDS
|
||||||
|
|
||||||
|
|
||||||
|
store_prefix = config.get('cashbook', 'store_prefix', default='cashbook')
|
||||||
|
image_limit = config.get('cashbook', 'image_max_pixel', default='2000')
|
||||||
|
try :
|
||||||
|
image_limit = int(image_limit)
|
||||||
|
if image_limit < 100:
|
||||||
|
image_limit = 100
|
||||||
|
if image_limit > 10000:
|
||||||
|
image_limit = 10000
|
||||||
|
except :
|
||||||
|
image_limit = 2000
|
||||||
|
|
||||||
|
|
||||||
|
class Line(metaclass=PoolMeta):
|
||||||
|
__name__ = 'cashbook.line'
|
||||||
|
|
||||||
|
media = fields.Binary(string='Image of PDF', filename='media_name',
|
||||||
|
file_id='media_id', store_prefix=store_prefix,
|
||||||
|
states=STATES, depends=DEPENDS)
|
||||||
|
media_name = fields.Char(string='File name',
|
||||||
|
states={
|
||||||
|
'required': Bool(Eval('media')),
|
||||||
|
'readonly': STATES['readonly'],
|
||||||
|
}, depends=DEPENDS)
|
||||||
|
media_id = fields.Char(string='File ID', readonly=True)
|
||||||
|
media_mime = fields.Char(string='MIME', readonly=True)
|
||||||
|
media_size = fields.Integer(string='File size', readonly=True)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _identify_file(cls, data, mime=True):
|
||||||
|
""" get file-type
|
||||||
|
"""
|
||||||
|
return magic.from_buffer(data, mime=mime)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _hr_file_size(cls, num, suffix="B"):
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]:
|
||||||
|
if abs(num) < 1024.0:
|
||||||
|
return f"{num:3.1f}{unit}{suffix}"
|
||||||
|
num /= 1024.0
|
||||||
|
return f"{num:.1f}Yi{suffix}"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def resize_image_file(cls, image_data):
|
||||||
|
""" shrink image 'image_limit' pixel if its bigger
|
||||||
|
"""
|
||||||
|
image_data2 = None
|
||||||
|
with BytesIO(image_data) as fhdl:
|
||||||
|
image = Image.open(fhdl, 'r')
|
||||||
|
|
||||||
|
(width, height) = image.size
|
||||||
|
if (width > image_limit) or (height > image_limit):
|
||||||
|
|
||||||
|
if width > height:
|
||||||
|
new_size = (image_limit, int(height * image_limit / width))
|
||||||
|
else :
|
||||||
|
new_size = (int(width * image_limit / height), image_limit)
|
||||||
|
|
||||||
|
# resize - fit in (image_limit x image_limit)
|
||||||
|
img2 = image.resize(new_size, Image.LANCZOS)
|
||||||
|
with BytesIO() as fhdl2:
|
||||||
|
img2.save(fhdl2, 'JPEG', optimize=True, quality=80)
|
||||||
|
fhdl2.seek(0)
|
||||||
|
image_data2 = fhdl2.read()
|
||||||
|
del img2
|
||||||
|
del image
|
||||||
|
|
||||||
|
return image_data2
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_media_info(cls, values):
|
||||||
|
""" get mime-type, update file-name
|
||||||
|
"""
|
||||||
|
if len(values['media'] or '') < 100:
|
||||||
|
values['media'] = None
|
||||||
|
values['media_mime'] = None
|
||||||
|
values['media_size'] = None
|
||||||
|
values['media_name'] = None
|
||||||
|
else :
|
||||||
|
values['media_mime'] = cls._identify_file(values['media'][:1024])
|
||||||
|
|
||||||
|
# if its a image, resize it to fit in (image_limit x image_limit) pixel
|
||||||
|
if values['media_mime'].startswith('image'):
|
||||||
|
new_image = cls.resize_image_file(values['media'])
|
||||||
|
if new_image is not None:
|
||||||
|
values['media'] = new_image
|
||||||
|
values['media_mime'] = cls._identify_file(values['media'][:1024])
|
||||||
|
|
||||||
|
values['media_size'] = len(values['media'])
|
||||||
|
file_ext = mimetypes.guess_extension(values['media_mime'])
|
||||||
|
if 'media_name' in values.keys():
|
||||||
|
if not values['media_name'].endswith(file_ext):
|
||||||
|
# cut extension
|
||||||
|
if values['media_name'][-4] == '.':
|
||||||
|
values['media_name'] = values['media_name'][:-4]
|
||||||
|
values['media_name'] = values['media_name'] + file_ext
|
||||||
|
return values
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def validate(cls, lines):
|
||||||
|
""" deny invalid mime-types, file-sizes etc.
|
||||||
|
"""
|
||||||
|
super(Line, cls).validate(lines)
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
if line.media is not None:
|
||||||
|
if line.media_size > 1024*1024*5:
|
||||||
|
raise UserError(gettext(
|
||||||
|
'cashbook_media.msg_file_too_big',
|
||||||
|
recname = line.rec_name,
|
||||||
|
))
|
||||||
|
if not line.media_mime in ['application/pdf',
|
||||||
|
'image/png', 'image/jpg', 'image/jpeg']:
|
||||||
|
raise UserError(gettext(
|
||||||
|
'cashbook_media.msg_file_invalid_mime',
|
||||||
|
recname = line.rec_name,
|
||||||
|
fmime = line.media_mime,
|
||||||
|
))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(cls, vlist):
|
||||||
|
""" add media-info
|
||||||
|
"""
|
||||||
|
vlist = [x.copy() for x in vlist]
|
||||||
|
for values in vlist:
|
||||||
|
if 'media' in values.keys():
|
||||||
|
values.update(cls.get_media_info(values))
|
||||||
|
return super(Line, cls).create(vlist)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def write(cls, *args):
|
||||||
|
""" update media-info
|
||||||
|
"""
|
||||||
|
actions = iter(args)
|
||||||
|
to_write = []
|
||||||
|
for records, values in zip(actions, actions):
|
||||||
|
if 'media' in values.keys():
|
||||||
|
values.update(cls.get_media_info(values))
|
||||||
|
|
||||||
|
to_write.extend([
|
||||||
|
records,
|
||||||
|
values,
|
||||||
|
])
|
||||||
|
super(Line, cls).write(*to_write)
|
||||||
|
|
||||||
|
# end Line
|
15
line.xml
Normal file
15
line.xml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!-- This file is part of the cashbook-module from m-ds for Tryton.
|
||||||
|
The COPYRIGHT file at the top level of this repository contains the
|
||||||
|
full copyright notices and license terms. -->
|
||||||
|
<tryton>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="line_view_form">
|
||||||
|
<field name="model">cashbook.line</field>
|
||||||
|
<field name="inherit" ref="cashbook.line_view_form"/>
|
||||||
|
<field name="name">line_form</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</tryton>
|
44
locale/de.po
Normal file
44
locale/de.po
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||||
|
|
||||||
|
|
||||||
|
##############
|
||||||
|
# ir.message #
|
||||||
|
##############
|
||||||
|
msgctxt "model:ir.message,text:msg_file_too_big"
|
||||||
|
msgid "The file size of the record '%(recname)s' exceeded the maximum value of 5MB."
|
||||||
|
msgstr "Die Dateigröße des Datensatzes '%(recname)s' überschreitete den maximalen Wert von 5MB."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_file_invalid_mime"
|
||||||
|
msgid "The file type '%(fmime)s' of the record '%(recname)s' is not allowed. (allowed: PNG, JPG, PDF)"
|
||||||
|
msgstr "Der Dateityp '%(fmime)s' des Datensatzes '%(recname)s' ist nicht zugelassen. (erlaubt: PNG, JPG, PDF)"
|
||||||
|
|
||||||
|
|
||||||
|
#################
|
||||||
|
# cashbook.line #
|
||||||
|
#################
|
||||||
|
msgctxt "view:cashbook.line:"
|
||||||
|
msgid "Image/PDF"
|
||||||
|
msgstr "Bild/PDF"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.line,media:"
|
||||||
|
msgid "Image of PDF"
|
||||||
|
msgstr "Bild oder PDF"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.line,media_name:"
|
||||||
|
msgid "File name"
|
||||||
|
msgstr "Dateiname"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.line,media_id:"
|
||||||
|
msgid "File ID"
|
||||||
|
msgstr "Datei-ID"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.line,media_mime:"
|
||||||
|
msgid "MIME"
|
||||||
|
msgstr "MIME"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.line,media_size:"
|
||||||
|
msgid "File size"
|
||||||
|
msgstr "Dateigröße"
|
||||||
|
|
36
locale/en.po
Normal file
36
locale/en.po
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr "Content-Type: text/plain; charset=utf-8\n"
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_file_too_big"
|
||||||
|
msgid "The file size of the record '%(recname)s' exceeded the maximum value of 5MB."
|
||||||
|
msgstr "The file size of the record '%(recname)s' exceeded the maximum value of 5MB."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_file_invalid_mime"
|
||||||
|
msgid "The file type '%(fmime)s' of the record '%(recname)s' is not allowed. (allowed: PNG, JPG, PDF)"
|
||||||
|
msgstr "The file type '%(fmime)s' of the record '%(recname)s' is not allowed. (allowed: PNG, JPG, PDF)"
|
||||||
|
|
||||||
|
msgctxt "view:cashbook.line:"
|
||||||
|
msgid "Image/PDF"
|
||||||
|
msgstr "Image/PDF"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.line,media:"
|
||||||
|
msgid "Image of PDF"
|
||||||
|
msgstr "Image of PDF"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.line,media_name:"
|
||||||
|
msgid "File name"
|
||||||
|
msgstr "File name"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.line,media_id:"
|
||||||
|
msgid "File ID"
|
||||||
|
msgstr "File ID"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.line,media_mime:"
|
||||||
|
msgid "MIME"
|
||||||
|
msgstr "MIME"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.line,media_size:"
|
||||||
|
msgid "File size"
|
||||||
|
msgstr "File size"
|
||||||
|
|
16
message.xml
Normal file
16
message.xml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!-- This file is part of the cashbook-module from m-ds for Tryton.
|
||||||
|
The COPYRIGHT file at the top level of this repository contains the
|
||||||
|
full copyright notices and license terms. -->
|
||||||
|
<tryton>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<record model="ir.message" id="msg_file_too_big">
|
||||||
|
<field name="text">The file size of the record '%(recname)s' exceeded the maximum value of 5MB.</field>
|
||||||
|
</record>
|
||||||
|
<record model="ir.message" id="msg_file_invalid_mime">
|
||||||
|
<field name="text">The file type '%(fmime)s' of the record '%(recname)s' is not allowed. (allowed: PNG, JPG, PDF)</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</tryton>
|
2
setup.py
2
setup.py
|
@ -42,7 +42,7 @@ with open(path.join(here, 'versiondep.txt'), encoding='utf-8') as f:
|
||||||
major_version = 6
|
major_version = 6
|
||||||
minor_version = 0
|
minor_version = 0
|
||||||
|
|
||||||
requires = []
|
requires = ['python-magic>=0.4.24', 'Pillow']
|
||||||
for dep in info.get('depends', []):
|
for dep in info.get('depends', []):
|
||||||
if not re.match(r'(ir|res|webdav)(\W|$)', dep):
|
if not re.match(r'(ir|res|webdav)(\W|$)', dep):
|
||||||
if dep in modversion.keys():
|
if dep in modversion.keys():
|
||||||
|
|
24
tests/__init__.py
Normal file
24
tests/__init__.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||||
|
# this repository contains the full copyright notices and license terms.
|
||||||
|
|
||||||
|
import trytond.tests.test_tryton
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from trytond.modules.cashbook_media.tests.test_line import LineTestCase
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['suite']
|
||||||
|
|
||||||
|
|
||||||
|
class CashbookTestCase(\
|
||||||
|
LineTestCase,
|
||||||
|
):
|
||||||
|
'Test cashbook module'
|
||||||
|
module = 'cashbook_media'
|
||||||
|
|
||||||
|
# end CashbookTestCase
|
||||||
|
|
||||||
|
def suite():
|
||||||
|
suite = trytond.tests.test_tryton.suite()
|
||||||
|
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(CashbookTestCase))
|
||||||
|
return suite
|
5
tests/img_data.py
Normal file
5
tests/img_data.py
Normal file
File diff suppressed because one or more lines are too long
228
tests/test_line.py
Normal file
228
tests/test_line.py
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# This file is part of the cashbook-module from m-ds for Tryton.
|
||||||
|
# The COPYRIGHT file at the top level of this repository contains the
|
||||||
|
# full copyright notices and license terms.
|
||||||
|
|
||||||
|
from io import BytesIO
|
||||||
|
from PIL import Image
|
||||||
|
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
|
||||||
|
from trytond.pool import Pool
|
||||||
|
from trytond.transaction import Transaction
|
||||||
|
from trytond.exceptions import UserError
|
||||||
|
from trytond.modules.cashbook.tests import CashbookTestCase
|
||||||
|
from datetime import date
|
||||||
|
from decimal import Decimal
|
||||||
|
from .img_data import img_data_png, dok_data_pdf, text_data
|
||||||
|
|
||||||
|
|
||||||
|
class LineTestCase(CashbookTestCase):
|
||||||
|
'Test cashbook line module'
|
||||||
|
module = 'cashbook_media'
|
||||||
|
|
||||||
|
@with_transaction()
|
||||||
|
def test_media_add_image(self):
|
||||||
|
""" create cook/line, add png-file
|
||||||
|
"""
|
||||||
|
pool = Pool()
|
||||||
|
Book = pool.get('cashbook.book')
|
||||||
|
Lines = pool.get('cashbook.line')
|
||||||
|
|
||||||
|
types = self.prep_type()
|
||||||
|
category = self.prep_category(cattype='in')
|
||||||
|
company = self.prep_company()
|
||||||
|
party = self.prep_party()
|
||||||
|
book, = Book.create([{
|
||||||
|
'name': 'Book 1',
|
||||||
|
'btype': types.id,
|
||||||
|
'company': company.id,
|
||||||
|
'currency': company.currency.id,
|
||||||
|
'number_sequ': self.prep_sequence().id,
|
||||||
|
'start_date': date(2022, 5, 1),
|
||||||
|
'lines': [('create', [{
|
||||||
|
'date': date(2022, 5, 1),
|
||||||
|
'description': 'Text 1',
|
||||||
|
'category': category.id,
|
||||||
|
'bookingtype': 'in',
|
||||||
|
'amount': Decimal('1.0'),
|
||||||
|
'party': party.id,
|
||||||
|
},])],
|
||||||
|
}])
|
||||||
|
self.assertEqual(book.name, 'Book 1')
|
||||||
|
self.assertEqual(len(book.lines), 1)
|
||||||
|
self.assertEqual(book.state, 'open')
|
||||||
|
|
||||||
|
# add image to line-1
|
||||||
|
Lines.write(*[
|
||||||
|
[book.lines[0]],
|
||||||
|
{
|
||||||
|
'media': img_data_png,
|
||||||
|
'media_name': 'image.png',
|
||||||
|
}])
|
||||||
|
self.assertEqual(book.lines[0].media_size, 18428)
|
||||||
|
self.assertEqual(book.lines[0].media_mime, 'image/png')
|
||||||
|
self.assertEqual(book.lines[0].media_name, 'image.png')
|
||||||
|
|
||||||
|
# replace image at line-1 by pdf
|
||||||
|
Lines.write(*[
|
||||||
|
[book.lines[0]],
|
||||||
|
{
|
||||||
|
'media': dok_data_pdf,
|
||||||
|
}])
|
||||||
|
self.assertEqual(book.lines[0].media_size, 8724)
|
||||||
|
self.assertEqual(book.lines[0].media_mime, 'application/pdf')
|
||||||
|
self.assertEqual(book.lines[0].media_name, 'image.png')
|
||||||
|
|
||||||
|
# create line with pdf
|
||||||
|
Book.write(*[
|
||||||
|
[book],
|
||||||
|
{
|
||||||
|
'lines': [('create', [{
|
||||||
|
'date': date(2022, 5, 2),
|
||||||
|
'description': 'Text 2',
|
||||||
|
'category': category.id,
|
||||||
|
'bookingtype': 'in',
|
||||||
|
'amount': Decimal('1.0'),
|
||||||
|
'party': party.id,
|
||||||
|
'media': dok_data_pdf,
|
||||||
|
'media_name': 'data.pdf',
|
||||||
|
}])],
|
||||||
|
}
|
||||||
|
])
|
||||||
|
self.assertEqual(len(book.lines), 2)
|
||||||
|
self.assertEqual(book.lines[1].media_size, 8724)
|
||||||
|
self.assertEqual(book.lines[1].media_mime, 'application/pdf')
|
||||||
|
self.assertEqual(book.lines[1].media_name, 'data.pdf')
|
||||||
|
|
||||||
|
@with_transaction()
|
||||||
|
def test_media_add_invalid_file(self):
|
||||||
|
""" create cook/line, add txt-file
|
||||||
|
"""
|
||||||
|
pool = Pool()
|
||||||
|
Book = pool.get('cashbook.book')
|
||||||
|
Lines = pool.get('cashbook.line')
|
||||||
|
|
||||||
|
types = self.prep_type()
|
||||||
|
category = self.prep_category(cattype='in')
|
||||||
|
company = self.prep_company()
|
||||||
|
party = self.prep_party()
|
||||||
|
book, = Book.create([{
|
||||||
|
'name': 'Book 1',
|
||||||
|
'btype': types.id,
|
||||||
|
'company': company.id,
|
||||||
|
'currency': company.currency.id,
|
||||||
|
'number_sequ': self.prep_sequence().id,
|
||||||
|
'start_date': date(2022, 5, 1),
|
||||||
|
'lines': [('create', [{
|
||||||
|
'date': date(2022, 5, 1),
|
||||||
|
'description': 'Text 1',
|
||||||
|
'category': category.id,
|
||||||
|
'bookingtype': 'in',
|
||||||
|
'amount': Decimal('1.0'),
|
||||||
|
'party': party.id,
|
||||||
|
},])],
|
||||||
|
}])
|
||||||
|
self.assertEqual(book.name, 'Book 1')
|
||||||
|
self.assertEqual(len(book.lines), 1)
|
||||||
|
self.assertEqual(book.state, 'open')
|
||||||
|
|
||||||
|
# add invalid file
|
||||||
|
self.assertRaisesRegex(UserError,
|
||||||
|
"The file type 'text/plain' of the record '05/02/2022|Rev|1.00 usd|Text 2 [Cat1]' is not allowed. (allowed: PNG, JPG, PDF)",
|
||||||
|
Book.write,
|
||||||
|
*[
|
||||||
|
[book],
|
||||||
|
{
|
||||||
|
'lines': [('create', [{
|
||||||
|
'date': date(2022, 5, 2),
|
||||||
|
'description': 'Text 2',
|
||||||
|
'category': category.id,
|
||||||
|
'bookingtype': 'in',
|
||||||
|
'amount': Decimal('1.0'),
|
||||||
|
'party': party.id,
|
||||||
|
'media': text_data,
|
||||||
|
'media_name': 'text.txt',
|
||||||
|
}])],
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
# replace image at line-1 by invalid file
|
||||||
|
self.assertRaisesRegex(UserError,
|
||||||
|
"The file type 'text/plain' of the record '05/02/2022|Rev|1.00 usd|Text 2 [Cat1]' is not allowed. (allowed: PNG, JPG, PDF)",
|
||||||
|
Lines.write,
|
||||||
|
*[
|
||||||
|
[book.lines[0]],
|
||||||
|
{
|
||||||
|
'media': text_data,
|
||||||
|
'media_name': 'text.txt',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
@with_transaction()
|
||||||
|
def test_media_add_big_file(self):
|
||||||
|
""" create cook/line, add big png-file
|
||||||
|
"""
|
||||||
|
pool = Pool()
|
||||||
|
Book = pool.get('cashbook.book')
|
||||||
|
Lines = pool.get('cashbook.line')
|
||||||
|
|
||||||
|
types = self.prep_type()
|
||||||
|
category = self.prep_category(cattype='in')
|
||||||
|
company = self.prep_company()
|
||||||
|
party = self.prep_party()
|
||||||
|
|
||||||
|
book, = Book.create([{
|
||||||
|
'name': 'Book 1',
|
||||||
|
'btype': types.id,
|
||||||
|
'company': company.id,
|
||||||
|
'currency': company.currency.id,
|
||||||
|
'number_sequ': self.prep_sequence().id,
|
||||||
|
'start_date': date(2022, 5, 1),
|
||||||
|
'lines': [('create', [{
|
||||||
|
'date': date(2022, 5, 1),
|
||||||
|
'description': 'Text 1',
|
||||||
|
'category': category.id,
|
||||||
|
'bookingtype': 'in',
|
||||||
|
'amount': Decimal('1.0'),
|
||||||
|
'party': party.id,
|
||||||
|
},])],
|
||||||
|
}])
|
||||||
|
self.assertEqual(book.name, 'Book 1')
|
||||||
|
self.assertEqual(len(book.lines), 1)
|
||||||
|
self.assertEqual(book.state, 'open')
|
||||||
|
|
||||||
|
# construct image
|
||||||
|
with BytesIO() as fhdl:
|
||||||
|
img1 = Image.new('RGB', (3200, 1340))
|
||||||
|
img1.save(fhdl, 'PNG', optimize=True)
|
||||||
|
del img1
|
||||||
|
|
||||||
|
fhdl.seek(0)
|
||||||
|
img_big_data = fhdl.read()
|
||||||
|
|
||||||
|
# create line with png, should be resized
|
||||||
|
Book.write(*[
|
||||||
|
[book],
|
||||||
|
{
|
||||||
|
'lines': [('create', [{
|
||||||
|
'date': date(2022, 5, 2),
|
||||||
|
'description': 'Text 2',
|
||||||
|
'category': category.id,
|
||||||
|
'bookingtype': 'in',
|
||||||
|
'amount': Decimal('1.0'),
|
||||||
|
'party': party.id,
|
||||||
|
'media': img_big_data,
|
||||||
|
'media_name': 'big.png',
|
||||||
|
}])],
|
||||||
|
}
|
||||||
|
])
|
||||||
|
self.assertEqual(len(book.lines), 2)
|
||||||
|
self.assertEqual(book.lines[1].media_mime, 'image/jpeg')
|
||||||
|
self.assertEqual(book.lines[1].media_size, 10221)
|
||||||
|
self.assertEqual(book.lines[1].media_name, 'big.jpg')
|
||||||
|
|
||||||
|
# check image size
|
||||||
|
with BytesIO(book.lines[1].media) as fhdl:
|
||||||
|
img2 = Image.open(fhdl, 'r')
|
||||||
|
self.assertEqual(img2.size, (2000, 837))
|
||||||
|
|
||||||
|
# end LineTestCase
|
|
@ -3,3 +3,5 @@ version=6.0.0
|
||||||
depends:
|
depends:
|
||||||
cashbook
|
cashbook
|
||||||
xml:
|
xml:
|
||||||
|
message.xml
|
||||||
|
line.xml
|
||||||
|
|
24
view/line_form.xml
Normal file
24
view/line_form.xml
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!-- This file is part of the cashbook-module from m-ds for Tryton.
|
||||||
|
The COPYRIGHT file at the top level of this repository contains the
|
||||||
|
full copyright notices and license terms. -->
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<xpath expr="/form/notebook/page[@name='references']" position="after">
|
||||||
|
|
||||||
|
<page name="media" col="4" string="Image/PDF">
|
||||||
|
<label name="media"/>
|
||||||
|
<field name="media"/>
|
||||||
|
<newline/>
|
||||||
|
|
||||||
|
<label name="media_name"/>
|
||||||
|
<field name="media_name"/>
|
||||||
|
|
||||||
|
<label name="media_mime"/>
|
||||||
|
<field name="media_mime"/>
|
||||||
|
|
||||||
|
</page>
|
||||||
|
|
||||||
|
</xpath>
|
||||||
|
|
||||||
|
</data>
|
Loading…
Reference in a new issue