Compare commits
16 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
5791a1f443 | ||
![]() |
27860b60fb | ||
![]() |
d9958b5ccf | ||
![]() |
5c22c0fbb3 | ||
![]() |
40bde467fe | ||
![]() |
875946c793 | ||
![]() |
7aef8e2fad | ||
![]() |
411d7e52ae | ||
![]() |
9d0001f262 | ||
![]() |
a149f46dbb | ||
![]() |
3750a4b0f6 | ||
![]() |
89ad435e72 | ||
![]() |
51934f46bc | ||
![]() |
fa526fa69e | ||
![]() |
6aa9bb5325 | ||
![]() |
1286fa6f2f |
13 changed files with 144 additions and 74 deletions
|
@ -1,3 +1,6 @@
|
|||
syntax: glob
|
||||
__pycache__/*
|
||||
locale/convert_de2en.py
|
||||
build/*
|
||||
mds_cashbook_media.egg-info/*
|
||||
dist/*
|
||||
|
|
17
README.rst
17
README.rst
|
@ -14,6 +14,23 @@ Requires
|
|||
Changes
|
||||
=======
|
||||
|
||||
*6.0.4 - 06.06.2023*
|
||||
|
||||
- code optimized
|
||||
|
||||
*6.0.3 - 14.02.2023*
|
||||
|
||||
- updt: setup.py
|
||||
|
||||
*6.0.2 - 23.01.2023*
|
||||
|
||||
- add: show image in tab 'image/pdf'
|
||||
|
||||
*6.0.1 - 29.11.2022*
|
||||
|
||||
- fix: exception when image type unknown
|
||||
|
||||
*6.0.0 - 14.10.2022*
|
||||
|
||||
- init
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
from trytond.pool import Pool
|
||||
from .line import Line
|
||||
|
||||
|
||||
def register():
|
||||
Pool.register(
|
||||
Line,
|
||||
|
|
68
line.py
68
line.py
|
@ -3,13 +3,13 @@
|
|||
# The COPYRIGHT file at the top level of this repository contains the
|
||||
# full copyright notices and license terms.
|
||||
|
||||
import mimetypes, magic
|
||||
import mimetypes
|
||||
import magic
|
||||
from io import BytesIO
|
||||
from PIL import Image
|
||||
from PIL import Image, UnidentifiedImageError
|
||||
from trytond.model import fields
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pool import 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
|
||||
|
@ -18,30 +18,50 @@ 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 :
|
||||
try:
|
||||
image_limit = int(image_limit)
|
||||
if image_limit < 100:
|
||||
image_limit = 100
|
||||
if image_limit > 10000:
|
||||
image_limit = 10000
|
||||
except :
|
||||
except Exception:
|
||||
image_limit = 2000
|
||||
|
||||
|
||||
STATES2 = {}
|
||||
STATES2.update(STATES)
|
||||
DEPENDS2 = []
|
||||
DEPENDS2.extend(DEPENDS)
|
||||
|
||||
|
||||
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',
|
||||
media = fields.Binary(
|
||||
string='Image of PDF', filename='media_name', file_id='media_id',
|
||||
store_prefix=store_prefix, states=STATES2, depends=DEPENDS2)
|
||||
media_name = fields.Char(
|
||||
string='File name',
|
||||
states={
|
||||
'required': Bool(Eval('media')),
|
||||
'readonly': STATES['readonly'],
|
||||
}, depends=DEPENDS)
|
||||
'readonly': STATES2['readonly'],
|
||||
}, depends=DEPENDS2)
|
||||
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)
|
||||
media_image = fields.Function(fields.Binary(
|
||||
string='Image', readonly=True,
|
||||
states={
|
||||
'invisible': ~Eval('media_mime', '').in_([
|
||||
'image/png', 'image/jpg', 'image/jpeg']),
|
||||
}, depends=['media_mime']), 'on_change_with_media_image')
|
||||
|
||||
@fields.depends('media', 'media_mime')
|
||||
def on_change_with_media_image(self, name=True):
|
||||
""" return binary if its a image
|
||||
"""
|
||||
if (self.media_mime or '-').startswith('image/'):
|
||||
return self.media
|
||||
|
||||
@classmethod
|
||||
def _identify_file(cls, data, mime=True):
|
||||
|
@ -65,14 +85,17 @@ class Line(metaclass=PoolMeta):
|
|||
"""
|
||||
image_data2 = None
|
||||
with BytesIO(image_data) as fhdl:
|
||||
try:
|
||||
image = Image.open(fhdl, 'r')
|
||||
except UnidentifiedImageError:
|
||||
raise UserError(gettext('cashbook_media.msg_file_unknown_type'))
|
||||
|
||||
(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 :
|
||||
else:
|
||||
new_size = (int(width * image_limit / height), image_limit)
|
||||
|
||||
# resize - fit in (image_limit x image_limit)
|
||||
|
@ -95,15 +118,17 @@ class Line(metaclass=PoolMeta):
|
|||
values['media_mime'] = None
|
||||
values['media_size'] = None
|
||||
values['media_name'] = None
|
||||
else :
|
||||
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 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_mime'] = cls._identify_file(
|
||||
values['media'][:1024])
|
||||
|
||||
values['media_size'] = len(values['media'])
|
||||
file_ext = mimetypes.guess_extension(values['media_mime'])
|
||||
|
@ -126,15 +151,14 @@ class Line(metaclass=PoolMeta):
|
|||
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',
|
||||
recname=line.rec_name))
|
||||
if line.media_mime not 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,
|
||||
))
|
||||
recname=line.rec_name,
|
||||
fmime=line.media_mime))
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
|
|
|
@ -14,6 +14,10 @@ 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)"
|
||||
|
||||
msgctxt "model:ir.message,text:msg_file_unknown_type"
|
||||
msgid "cannot identify image file"
|
||||
msgstr "Bilddatei kann nicht identifiziert werden"
|
||||
|
||||
|
||||
#################
|
||||
# cashbook.line #
|
||||
|
@ -42,3 +46,6 @@ msgctxt "field:cashbook.line,media_size:"
|
|||
msgid "File size"
|
||||
msgstr "Dateigröße"
|
||||
|
||||
msgctxt "field:cashbook.line,media_image:"
|
||||
msgid "Image"
|
||||
msgstr "Bild"
|
||||
|
|
|
@ -10,6 +10,10 @@ 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 "model:ir.message,text:msg_file_unknown_type"
|
||||
msgid "cannot identify image file"
|
||||
msgstr "cannot identify image file"
|
||||
|
||||
msgctxt "view:cashbook.line:"
|
||||
msgid "Image/PDF"
|
||||
msgstr "Image/PDF"
|
||||
|
|
|
@ -11,6 +11,9 @@ full copyright notices and license terms. -->
|
|||
<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>
|
||||
<record model="ir.message" id="msg_file_unknown_type">
|
||||
<field name="text">cannot identify image file</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</tryton>
|
||||
|
|
36
setup.py
36
setup.py
|
@ -2,7 +2,7 @@
|
|||
"""
|
||||
|
||||
# Always prefer setuptools over distutils
|
||||
from setuptools import setup, find_packages
|
||||
from setuptools import setup
|
||||
# To use a consistent encoding
|
||||
from codecs import open
|
||||
from os import path
|
||||
|
@ -36,7 +36,7 @@ with open(path.join(here, 'versiondep.txt'), encoding='utf-8') as f:
|
|||
l2 = i.strip().split(';')
|
||||
if len(l2) < 4:
|
||||
continue
|
||||
modversion[l2[0]] = {'min':l2[1], 'max':l2[2], 'prefix':l2[3]}
|
||||
modversion[l2[0]] = {'min': l2[1], 'max': l2[2], 'prefix': l2[3]}
|
||||
|
||||
# tryton-version
|
||||
major_version = 6
|
||||
|
@ -51,23 +51,32 @@ for dep in info.get('depends', []):
|
|||
prefix = modversion[dep]['prefix']
|
||||
|
||||
if len(modversion[dep]['max']) > 0:
|
||||
requires.append('%s_%s >= %s, <= %s' %
|
||||
(prefix, dep, modversion[dep]['min'], modversion[dep]['max']))
|
||||
else :
|
||||
requires.append('%s_%s >= %s' %
|
||||
requires.append(
|
||||
'%s_%s >= %s, <= %s' %
|
||||
(prefix, dep,
|
||||
modversion[dep]['min'],
|
||||
modversion[dep]['max']))
|
||||
else:
|
||||
requires.append(
|
||||
'%s_%s >= %s' %
|
||||
(prefix, dep, modversion[dep]['min']))
|
||||
else :
|
||||
requires.append('%s_%s >= %s.%s, < %s.%s' %
|
||||
('trytond', dep, major_version, minor_version,
|
||||
else:
|
||||
requires.append(
|
||||
'%s_%s >= %s.%s, < %s.%s' % (
|
||||
'trytond', dep, major_version, minor_version,
|
||||
major_version, minor_version + 1))
|
||||
requires.append('trytond >= %s.%s, < %s.%s' %
|
||||
(major_version, minor_version, major_version, minor_version + 1))
|
||||
requires.append(
|
||||
'trytond >= %s.%s, < %s.%s' % (
|
||||
major_version, minor_version, major_version, minor_version + 1))
|
||||
|
||||
setup(name='%s_%s' % (PREFIX, MODULE),
|
||||
setup(
|
||||
name='%s_%s' % (PREFIX, MODULE),
|
||||
version=info.get('version', '0.0.1'),
|
||||
description='Tryton module to add a file-field to cashbook.',
|
||||
long_description=long_description,
|
||||
long_description_content_type='text/x-rst',
|
||||
url='https://www.m-ds.de/',
|
||||
download_url='https://scmdev.m-ds.de/Tryton/Extra/cashbook_media',
|
||||
author='martin-data services',
|
||||
author_email='service@m-ds.de',
|
||||
license='GPL-3',
|
||||
|
@ -95,7 +104,8 @@ setup(name='%s_%s' % (PREFIX, MODULE),
|
|||
'trytond.modules.%s' % MODULE,
|
||||
],
|
||||
package_data={
|
||||
'trytond.modules.%s' % MODULE: (info.get('xml', [])
|
||||
'trytond.modules.%s' % MODULE: (
|
||||
info.get('xml', [])
|
||||
+ ['tryton.cfg', 'locale/*.po', 'tests/*.py',
|
||||
'view/*.xml', 'versiondep.txt', 'README.rst']),
|
||||
},
|
||||
|
|
|
@ -4,21 +4,14 @@
|
|||
import trytond.tests.test_tryton
|
||||
import unittest
|
||||
|
||||
from trytond.modules.cashbook_media.tests.test_line import LineTestCase
|
||||
from .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))
|
||||
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(
|
||||
LineTestCase))
|
||||
return suite
|
||||
|
|
|
@ -5,11 +5,10 @@
|
|||
|
||||
from io import BytesIO
|
||||
from PIL import Image
|
||||
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
|
||||
from trytond.tests.test_tryton import 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 trytond.modules.cashbook.tests.test_module import CashbookTestCase
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
from .img_data import img_data_png, dok_data_pdf, text_data
|
||||
|
@ -126,8 +125,11 @@ class LineTestCase(CashbookTestCase):
|
|||
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)",
|
||||
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],
|
||||
|
@ -146,8 +148,11 @@ class LineTestCase(CashbookTestCase):
|
|||
])
|
||||
|
||||
# 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)",
|
||||
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]],
|
||||
|
@ -163,7 +168,6 @@ class LineTestCase(CashbookTestCase):
|
|||
"""
|
||||
pool = Pool()
|
||||
Book = pool.get('cashbook.book')
|
||||
Lines = pool.get('cashbook.line')
|
||||
|
||||
types = self.prep_type()
|
||||
category = self.prep_category(cattype='in')
|
||||
|
@ -226,3 +230,6 @@ class LineTestCase(CashbookTestCase):
|
|||
self.assertEqual(img2.size, (2000, 837))
|
||||
|
||||
# end LineTestCase
|
||||
|
||||
|
||||
del CashbookTestCase
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
[tryton]
|
||||
version=6.0.0
|
||||
version=6.0.4
|
||||
depends:
|
||||
cashbook
|
||||
xml:
|
||||
message.xml
|
||||
line.xml
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
cashbook;6.0.18;6.0.999;mds
|
||||
cashbook;6.0.28;6.0.999;mds
|
||||
|
|
|
@ -13,10 +13,10 @@ full copyright notices and license terms. -->
|
|||
|
||||
<label name="media_name"/>
|
||||
<field name="media_name"/>
|
||||
|
||||
<label name="media_mime"/>
|
||||
<field name="media_mime"/>
|
||||
|
||||
<field name="media_image" readonly="1" colspan="4" widget="image" xexpand="1" yexpand="1"/>
|
||||
</page>
|
||||
|
||||
</xpath>
|
||||
|
|
Loading…
Reference in a new issue