Merge pull request #292 from eReuse/feature/3416-transaction-notes
Feature/3416 transaction notes
This commit is contained in:
commit
1297766577
|
@ -28,7 +28,7 @@ from wtforms import (
|
||||||
from wtforms.fields import FormField
|
from wtforms.fields import FormField
|
||||||
|
|
||||||
from ereuse_devicehub.db import db
|
from ereuse_devicehub.db import db
|
||||||
from ereuse_devicehub.inventory.models import Transfer
|
from ereuse_devicehub.inventory.models import DeliveryNote, ReceiverNote, Transfer
|
||||||
from ereuse_devicehub.parser.models import SnapshotsLog
|
from ereuse_devicehub.parser.models import SnapshotsLog
|
||||||
from ereuse_devicehub.parser.parser import ParseSnapshotLsHw
|
from ereuse_devicehub.parser.parser import ParseSnapshotLsHw
|
||||||
from ereuse_devicehub.parser.schemas import Snapshot_lite
|
from ereuse_devicehub.parser.schemas import Snapshot_lite
|
||||||
|
@ -1181,5 +1181,128 @@ class EditTransferForm(TransferForm):
|
||||||
self.description.data = self._obj.description
|
self.description.data = self._obj.description
|
||||||
self.date.data = self._obj.date
|
self.date.data = self._obj.date
|
||||||
|
|
||||||
|
def validate(self, extra_validators=None):
|
||||||
|
is_valid = super().validate(extra_validators)
|
||||||
|
date = self.date.data
|
||||||
|
if date and date > datetime.datetime.now().date():
|
||||||
|
self.date.errors = ["You have to choose a date before today."]
|
||||||
|
is_valid = False
|
||||||
|
return is_valid
|
||||||
|
|
||||||
def set_obj(self, commit=True):
|
def set_obj(self, commit=True):
|
||||||
self.populate_obj(self._obj)
|
self.populate_obj(self._obj)
|
||||||
|
|
||||||
|
|
||||||
|
class NotesForm(FlaskForm):
|
||||||
|
number = StringField(
|
||||||
|
'Number',
|
||||||
|
[validators.Optional()],
|
||||||
|
render_kw={'class': "form-control"},
|
||||||
|
description="You can put a number for tracer of receiver or delivery",
|
||||||
|
)
|
||||||
|
date = DateField(
|
||||||
|
'Date',
|
||||||
|
[validators.Optional()],
|
||||||
|
render_kw={'class': "form-control"},
|
||||||
|
description="""Date when the transfer was do it""",
|
||||||
|
)
|
||||||
|
units = IntegerField(
|
||||||
|
'Units',
|
||||||
|
[validators.Optional()],
|
||||||
|
render_kw={'class': "form-control"},
|
||||||
|
description="Number of units",
|
||||||
|
)
|
||||||
|
weight = IntegerField(
|
||||||
|
'Weight',
|
||||||
|
[validators.Optional()],
|
||||||
|
render_kw={'class': "form-control"},
|
||||||
|
description="Weight expressed in Kg",
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.type = kwargs.pop('type', None)
|
||||||
|
lot_id = kwargs.pop('lot_id', None)
|
||||||
|
self._tmp_lot = Lot.query.filter(Lot.id == lot_id).one()
|
||||||
|
self._obj = None
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
if self._tmp_lot.transfer:
|
||||||
|
if self.type == 'Delivery':
|
||||||
|
self._obj = self._tmp_lot.transfer.delivery_note
|
||||||
|
if not self._obj:
|
||||||
|
self._obj = DeliveryNote(transfer_id=self._tmp_lot.transfer.id)
|
||||||
|
|
||||||
|
self.date.description = """Date when the delivery was do it."""
|
||||||
|
self.number.description = (
|
||||||
|
"""You can put a number for tracer of delivery note."""
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.type == 'Receiver':
|
||||||
|
self._obj = self._tmp_lot.transfer.receiver_note
|
||||||
|
if not self._obj:
|
||||||
|
self._obj = ReceiverNote(transfer_id=self._tmp_lot.transfer.id)
|
||||||
|
|
||||||
|
self.date.description = """Date when the receipt was do it."""
|
||||||
|
self.number.description = (
|
||||||
|
"""You can put a number for tracer of receiber note."""
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.is_editable():
|
||||||
|
self.number.render_kw.pop('disabled', None)
|
||||||
|
self.date.render_kw.pop('disabled', None)
|
||||||
|
self.units.render_kw.pop('disabled', None)
|
||||||
|
self.weight.render_kw.pop('disabled', None)
|
||||||
|
else:
|
||||||
|
disabled = {'disabled': "disabled"}
|
||||||
|
self.number.render_kw.update(disabled)
|
||||||
|
self.date.render_kw.update(disabled)
|
||||||
|
self.units.render_kw.update(disabled)
|
||||||
|
self.weight.render_kw.update(disabled)
|
||||||
|
|
||||||
|
if self._obj and not self.data['csrf_token']:
|
||||||
|
self.number.data = self._obj.number
|
||||||
|
self.date.data = self._obj.date
|
||||||
|
self.units.data = self._obj.units
|
||||||
|
self.weight.data = self._obj.weight
|
||||||
|
|
||||||
|
def is_editable(self):
|
||||||
|
if not self._tmp_lot.transfer:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self._tmp_lot.transfer.closed:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self._tmp_lot.transfer.code:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if self._tmp_lot.transfer.user_from == g.user and self.type == 'Receiver':
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self._tmp_lot.transfer.user_to == g.user and self.type == 'Delivery':
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def validate(self, extra_validators=None):
|
||||||
|
is_valid = super().validate(extra_validators)
|
||||||
|
date = self.date.data
|
||||||
|
if date and date > datetime.datetime.now().date():
|
||||||
|
self.date.errors = ["You have to choose a date before today."]
|
||||||
|
is_valid = False
|
||||||
|
|
||||||
|
if not self.is_editable():
|
||||||
|
is_valid = False
|
||||||
|
|
||||||
|
return is_valid
|
||||||
|
|
||||||
|
def save(self, commit=True):
|
||||||
|
if self._tmp_lot.transfer.closed:
|
||||||
|
return self._obj
|
||||||
|
|
||||||
|
self.populate_obj(self._obj)
|
||||||
|
db.session.add(self._obj)
|
||||||
|
|
||||||
|
if commit:
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return self._obj
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from citext import CIText
|
from citext import CIText
|
||||||
from sqlalchemy import Column
|
from sqlalchemy import Column, Integer
|
||||||
from sqlalchemy.dialects.postgresql import UUID
|
from sqlalchemy.dialects.postgresql import UUID
|
||||||
from sqlalchemy.orm import backref, relationship
|
from sqlalchemy.orm import backref, relationship
|
||||||
from teal.db import CASCADE_OWN
|
from teal.db import CASCADE_OWN
|
||||||
|
@ -23,8 +23,8 @@ class Transfer(Thing):
|
||||||
description = Column(CIText(), default='', nullable=True)
|
description = Column(CIText(), default='', nullable=True)
|
||||||
lot_id = db.Column(
|
lot_id = db.Column(
|
||||||
UUID(as_uuid=True),
|
UUID(as_uuid=True),
|
||||||
db.ForeignKey('lot.id', use_alter=True, name='lot_trade'),
|
db.ForeignKey('lot.id', use_alter=True, name='lot_transfer'),
|
||||||
nullable=True,
|
nullable=False,
|
||||||
)
|
)
|
||||||
lot = relationship(
|
lot = relationship(
|
||||||
'Lot',
|
'Lot',
|
||||||
|
@ -42,3 +42,41 @@ class Transfer(Thing):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class DeliveryNote(Thing):
|
||||||
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
|
||||||
|
number = Column(CIText(), default='', nullable=False)
|
||||||
|
date = Column(db.TIMESTAMP(timezone=True))
|
||||||
|
units = Column(Integer, default=0)
|
||||||
|
weight = Column(Integer, default=0)
|
||||||
|
|
||||||
|
transfer_id = db.Column(
|
||||||
|
UUID(as_uuid=True),
|
||||||
|
db.ForeignKey('transfer.id'),
|
||||||
|
nullable=False,
|
||||||
|
)
|
||||||
|
transfer = relationship(
|
||||||
|
'Transfer',
|
||||||
|
backref=backref('delivery_note', lazy=True, uselist=False, cascade=CASCADE_OWN),
|
||||||
|
primaryjoin='DeliveryNote.transfer_id == Transfer.id',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ReceiverNote(Thing):
|
||||||
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
|
||||||
|
number = Column(CIText(), default='', nullable=False)
|
||||||
|
date = Column(db.TIMESTAMP(timezone=True))
|
||||||
|
units = Column(Integer, default=0)
|
||||||
|
weight = Column(Integer, default=0)
|
||||||
|
|
||||||
|
transfer_id = db.Column(
|
||||||
|
UUID(as_uuid=True),
|
||||||
|
db.ForeignKey('transfer.id'),
|
||||||
|
nullable=False,
|
||||||
|
)
|
||||||
|
transfer = relationship(
|
||||||
|
'Transfer',
|
||||||
|
backref=backref('receiver_note', lazy=True, uselist=False, cascade=CASCADE_OWN),
|
||||||
|
primaryjoin='ReceiverNote.transfer_id == Transfer.id',
|
||||||
|
)
|
||||||
|
|
|
@ -21,6 +21,7 @@ from ereuse_devicehub.inventory.forms import (
|
||||||
LotForm,
|
LotForm,
|
||||||
NewActionForm,
|
NewActionForm,
|
||||||
NewDeviceForm,
|
NewDeviceForm,
|
||||||
|
NotesForm,
|
||||||
TagDeviceForm,
|
TagDeviceForm,
|
||||||
TradeDocumentForm,
|
TradeDocumentForm,
|
||||||
TradeForm,
|
TradeForm,
|
||||||
|
@ -52,11 +53,15 @@ class DeviceListMixin(GenericMixin):
|
||||||
devices = form_filter.search()
|
devices = form_filter.search()
|
||||||
lot = None
|
lot = None
|
||||||
form_transfer = ''
|
form_transfer = ''
|
||||||
|
form_delivery = ''
|
||||||
|
form_receiver = ''
|
||||||
|
|
||||||
if lot_id:
|
if lot_id:
|
||||||
lot = lots.filter(Lot.id == lot_id).one()
|
lot = lots.filter(Lot.id == lot_id).one()
|
||||||
if not lot.is_temporary and lot.transfer:
|
if not lot.is_temporary and lot.transfer:
|
||||||
form_transfer = EditTransferForm(lot_id=lot.id)
|
form_transfer = EditTransferForm(lot_id=lot.id)
|
||||||
|
form_delivery = NotesForm(lot_id=lot.id, type='Delivery')
|
||||||
|
form_receiver = NotesForm(lot_id=lot.id, type='Receiver')
|
||||||
|
|
||||||
form_new_action = NewActionForm(lot=lot_id)
|
form_new_action = NewActionForm(lot=lot_id)
|
||||||
self.context.update(
|
self.context.update(
|
||||||
|
@ -67,6 +72,8 @@ class DeviceListMixin(GenericMixin):
|
||||||
'form_new_allocate': AllocateForm(lot=lot_id),
|
'form_new_allocate': AllocateForm(lot=lot_id),
|
||||||
'form_new_datawipe': DataWipeForm(lot=lot_id),
|
'form_new_datawipe': DataWipeForm(lot=lot_id),
|
||||||
'form_transfer': form_transfer,
|
'form_transfer': form_transfer,
|
||||||
|
'form_delivery': form_delivery,
|
||||||
|
'form_receiver': form_receiver,
|
||||||
'form_filter': form_filter,
|
'form_filter': form_filter,
|
||||||
'form_print_labels': PrintLabelsForm(),
|
'form_print_labels': PrintLabelsForm(),
|
||||||
'lot': lot,
|
'lot': lot,
|
||||||
|
@ -453,6 +460,10 @@ class EditTransferView(GenericMixin):
|
||||||
return flask.redirect(next_url)
|
return flask.redirect(next_url)
|
||||||
|
|
||||||
messages.error('Transfer updated error!')
|
messages.error('Transfer updated error!')
|
||||||
|
for k, v in form.errors.items():
|
||||||
|
value = ';'.join(v)
|
||||||
|
key = form[k].label.text
|
||||||
|
messages.error('Error {key}: {value}!'.format(key=key, value=value))
|
||||||
return flask.redirect(next_url)
|
return flask.redirect(next_url)
|
||||||
|
|
||||||
|
|
||||||
|
@ -631,6 +642,50 @@ class SnapshotDetailView(GenericMixin):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DeliveryNoteView(GenericMixin):
|
||||||
|
methods = ['POST']
|
||||||
|
form_class = NotesForm
|
||||||
|
|
||||||
|
def dispatch_request(self, lot_id):
|
||||||
|
self.get_context()
|
||||||
|
form = self.form_class(request.form, lot_id=lot_id, type='Delivery')
|
||||||
|
next_url = url_for('inventory.lotdevicelist', lot_id=lot_id)
|
||||||
|
|
||||||
|
if form.validate_on_submit():
|
||||||
|
form.save()
|
||||||
|
messages.success('Delivery Note updated successfully!')
|
||||||
|
return flask.redirect(next_url)
|
||||||
|
|
||||||
|
messages.error('Delivery Note updated error!')
|
||||||
|
for k, v in form.errors.items():
|
||||||
|
value = ';'.join(v)
|
||||||
|
key = form[k].label.text
|
||||||
|
messages.error('Error {key}: {value}!'.format(key=key, value=value))
|
||||||
|
return flask.redirect(next_url)
|
||||||
|
|
||||||
|
|
||||||
|
class ReceiverNoteView(GenericMixin):
|
||||||
|
methods = ['POST']
|
||||||
|
form_class = NotesForm
|
||||||
|
|
||||||
|
def dispatch_request(self, lot_id):
|
||||||
|
self.get_context()
|
||||||
|
form = self.form_class(request.form, lot_id=lot_id, type='Receiver')
|
||||||
|
next_url = url_for('inventory.lotdevicelist', lot_id=lot_id)
|
||||||
|
|
||||||
|
if form.validate_on_submit():
|
||||||
|
form.save()
|
||||||
|
messages.success('Receiver Note updated successfully!')
|
||||||
|
return flask.redirect(next_url)
|
||||||
|
|
||||||
|
messages.error('Receiver Note updated error!')
|
||||||
|
for k, v in form.errors.items():
|
||||||
|
value = ';'.join(v)
|
||||||
|
key = form[k].label.text
|
||||||
|
messages.error('Error {key}: {value}!'.format(key=key, value=value))
|
||||||
|
return flask.redirect(next_url)
|
||||||
|
|
||||||
|
|
||||||
devices.add_url_rule('/action/add/', view_func=NewActionView.as_view('action_add'))
|
devices.add_url_rule('/action/add/', view_func=NewActionView.as_view('action_add'))
|
||||||
devices.add_url_rule('/action/trade/add/', view_func=NewTradeView.as_view('trade_add'))
|
devices.add_url_rule('/action/trade/add/', view_func=NewTradeView.as_view('trade_add'))
|
||||||
devices.add_url_rule(
|
devices.add_url_rule(
|
||||||
|
@ -693,3 +748,11 @@ devices.add_url_rule(
|
||||||
'/lot/<string:lot_id>/transfer/',
|
'/lot/<string:lot_id>/transfer/',
|
||||||
view_func=EditTransferView.as_view('edit_transfer'),
|
view_func=EditTransferView.as_view('edit_transfer'),
|
||||||
)
|
)
|
||||||
|
devices.add_url_rule(
|
||||||
|
'/lot/<string:lot_id>/deliverynote/',
|
||||||
|
view_func=DeliveryNoteView.as_view('delivery_note'),
|
||||||
|
)
|
||||||
|
devices.add_url_rule(
|
||||||
|
'/lot/<string:lot_id>/receivernote/',
|
||||||
|
view_func=ReceiverNoteView.as_view('receiver_note'),
|
||||||
|
)
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
"""transfer notes
|
||||||
|
|
||||||
|
Revision ID: dac62da1621a
|
||||||
|
Revises: 054a3aea9f08
|
||||||
|
Create Date: 2022-06-03 12:04:39.486276
|
||||||
|
|
||||||
|
"""
|
||||||
|
import citext
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from alembic import context, op
|
||||||
|
from sqlalchemy.dialects import postgresql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'dac62da1621a'
|
||||||
|
down_revision = '054a3aea9f08'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def get_inv():
|
||||||
|
INV = context.get_x_argument(as_dictionary=True).get('inventory')
|
||||||
|
if not INV:
|
||||||
|
raise ValueError("Inventory value is not specified")
|
||||||
|
return INV
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# creating delivery note table
|
||||||
|
op.create_table(
|
||||||
|
'delivery_note',
|
||||||
|
sa.Column(
|
||||||
|
'updated',
|
||||||
|
sa.TIMESTAMP(timezone=True),
|
||||||
|
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||||
|
nullable=False,
|
||||||
|
),
|
||||||
|
sa.Column(
|
||||||
|
'created',
|
||||||
|
sa.TIMESTAMP(timezone=True),
|
||||||
|
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||||
|
nullable=False,
|
||||||
|
),
|
||||||
|
sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False),
|
||||||
|
sa.Column('date', sa.TIMESTAMP(timezone=True), nullable=True),
|
||||||
|
sa.Column('number', citext.CIText(), nullable=True),
|
||||||
|
sa.Column('weight', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('units', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('transfer_id', postgresql.UUID(as_uuid=True), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['transfer_id'], [f'{get_inv()}.transfer.id']),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
schema=f'{get_inv()}',
|
||||||
|
)
|
||||||
|
|
||||||
|
# creating index
|
||||||
|
op.create_index(
|
||||||
|
op.f('ix_delivery_note_created'),
|
||||||
|
'delivery_note',
|
||||||
|
['created'],
|
||||||
|
unique=False,
|
||||||
|
schema=f'{get_inv()}',
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
op.f('ix_delivery_note_updated'),
|
||||||
|
'delivery_note',
|
||||||
|
['updated'],
|
||||||
|
unique=False,
|
||||||
|
schema=f'{get_inv()}',
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
'ix_delivery_note_id',
|
||||||
|
'delivery_note',
|
||||||
|
['id'],
|
||||||
|
unique=False,
|
||||||
|
postgresql_using='hash',
|
||||||
|
schema=f'{get_inv()}',
|
||||||
|
)
|
||||||
|
|
||||||
|
# creating receiver note table
|
||||||
|
op.create_table(
|
||||||
|
'receiver_note',
|
||||||
|
sa.Column(
|
||||||
|
'updated',
|
||||||
|
sa.TIMESTAMP(timezone=True),
|
||||||
|
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||||
|
nullable=False,
|
||||||
|
),
|
||||||
|
sa.Column(
|
||||||
|
'created',
|
||||||
|
sa.TIMESTAMP(timezone=True),
|
||||||
|
server_default=sa.text('CURRENT_TIMESTAMP'),
|
||||||
|
nullable=False,
|
||||||
|
),
|
||||||
|
sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False),
|
||||||
|
sa.Column('date', sa.TIMESTAMP(timezone=True), nullable=True),
|
||||||
|
sa.Column('number', citext.CIText(), nullable=True),
|
||||||
|
sa.Column('weight', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('units', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('transfer_id', postgresql.UUID(as_uuid=True), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(['transfer_id'], [f'{get_inv()}.transfer.id']),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
schema=f'{get_inv()}',
|
||||||
|
)
|
||||||
|
|
||||||
|
# creating index
|
||||||
|
op.create_index(
|
||||||
|
op.f('ix_receiver_note_created'),
|
||||||
|
'receiver_note',
|
||||||
|
['created'],
|
||||||
|
unique=False,
|
||||||
|
schema=f'{get_inv()}',
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
op.f('ix_receiver_note_updated'),
|
||||||
|
'receiver_note',
|
||||||
|
['updated'],
|
||||||
|
unique=False,
|
||||||
|
schema=f'{get_inv()}',
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
'ix_receiver_note_id',
|
||||||
|
'receiver_note',
|
||||||
|
['id'],
|
||||||
|
unique=False,
|
||||||
|
postgresql_using='hash',
|
||||||
|
schema=f'{get_inv()}',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_index(
|
||||||
|
op.f('ix_delivery_note_created'),
|
||||||
|
table_name='delivery_note',
|
||||||
|
schema=f'{get_inv()}',
|
||||||
|
)
|
||||||
|
op.drop_index(
|
||||||
|
op.f('ix_delivery_note_updated'),
|
||||||
|
table_name='delivery_note',
|
||||||
|
schema=f'{get_inv()}',
|
||||||
|
)
|
||||||
|
op.drop_index(
|
||||||
|
op.f('ix_delivery_note_id'), table_name='delivery_note', schema=f'{get_inv()}'
|
||||||
|
)
|
||||||
|
op.drop_table('delivery_note', schema=f'{get_inv()}')
|
||||||
|
|
||||||
|
op.drop_index(
|
||||||
|
op.f('ix_receiver_note_created'),
|
||||||
|
table_name='receiver_note',
|
||||||
|
schema=f'{get_inv()}',
|
||||||
|
)
|
||||||
|
op.drop_index(
|
||||||
|
op.f('ix_receiver_note_updated'),
|
||||||
|
table_name='receiver_note',
|
||||||
|
schema=f'{get_inv()}',
|
||||||
|
)
|
||||||
|
op.drop_index(
|
||||||
|
op.f('ix_receiver_note_id'), table_name='receiver_note', schema=f'{get_inv()}'
|
||||||
|
)
|
||||||
|
op.drop_table('receiver_note', schema=f'{get_inv()}')
|
|
@ -90,6 +90,16 @@
|
||||||
Transfer ({% if lot.transfer.closed %}<span class="text-danger">Closed</span>{% else %}<span class="text-success">Open</span>{% endif %})
|
Transfer ({% if lot.transfer.closed %}<span class="text-danger">Closed</span>{% else %}<span class="text-success">Open</span>{% endif %})
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#edit-delivery-note">
|
||||||
|
Delivery Note
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#edit-receiver-note">
|
||||||
|
Receiver Note
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -480,6 +490,7 @@
|
||||||
<span class="text-danger">*</span>
|
<span class="text-danger">*</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ field }}
|
{{ field }}
|
||||||
|
<small class="text-muted">{{ field.description }}</small>
|
||||||
{% if field.errors %}
|
{% if field.errors %}
|
||||||
<p class="text-danger">
|
<p class="text-danger">
|
||||||
{% for error in field.errors %}
|
{% for error in field.errors %}
|
||||||
|
@ -498,6 +509,70 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="edit-delivery-note" class="tab-pane fade edit-delivery-note">
|
||||||
|
<h5 class="card-title">Delivery Note</h5>
|
||||||
|
<form method="post" action="{{ url_for('inventory.delivery_note', lot_id=lot.id) }}" class="row g-3 needs-validation" novalidate>
|
||||||
|
{{ form_delivery.csrf_token }}
|
||||||
|
|
||||||
|
{% for field in form_delivery %}
|
||||||
|
{% if field != form_delivery.csrf_token %}
|
||||||
|
<div class="col-12">
|
||||||
|
{% if field != form_delivery.type %}
|
||||||
|
{{ field.label(class_="form-label") }}
|
||||||
|
{{ field }}
|
||||||
|
<small class="text-muted">{{ field.description }}</small>
|
||||||
|
{% if field.errors %}
|
||||||
|
<p class="text-danger">
|
||||||
|
{% for error in field.errors %}
|
||||||
|
{{ error }}<br/>
|
||||||
|
{% endfor %}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% if lot.transfer and form_receiver.is_editable() %}
|
||||||
|
<div>
|
||||||
|
<a href="{{ url_for('inventory.lotdevicelist', lot_id=lot.id) }}" class="btn btn-danger">Cancel</a>
|
||||||
|
<button class="btn btn-primary" type="submit">Save</button>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div id="edit-receiver-note" class="tab-pane fade edit-receiver-note">
|
||||||
|
<h5 class="card-title">Receiver Note</h5>
|
||||||
|
<form method="post" action="{{ url_for('inventory.receiver_note', lot_id=lot.id) }}" class="row g-3 needs-validation" novalidate>
|
||||||
|
{{ form_receiver.csrf_token }}
|
||||||
|
|
||||||
|
{% for field in form_receiver %}
|
||||||
|
{% if field != form_receiver.csrf_token %}
|
||||||
|
<div class="col-12">
|
||||||
|
{% if field != form_receiver.type %}
|
||||||
|
{{ field.label(class_="form-label") }}
|
||||||
|
{{ field }}
|
||||||
|
<small class="text-muted">{{ field.description }}</small>
|
||||||
|
{% if field.errors %}
|
||||||
|
<p class="text-danger">
|
||||||
|
{% for error in field.errors %}
|
||||||
|
{{ error }}<br/>
|
||||||
|
{% endfor %}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% if lot.transfer and form_receiver.is_editable() %}
|
||||||
|
<div>
|
||||||
|
<a href="{{ url_for('inventory.lotdevicelist', lot_id=lot.id) }}" class="btn btn-danger">Cancel</a>
|
||||||
|
<button class="btn btn-primary" type="submit">Save</button>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div><!-- End Bordered Tabs -->
|
</div><!-- End Bordered Tabs -->
|
||||||
|
|
|
@ -61,6 +61,8 @@ def test_api_docs(client: Client):
|
||||||
'/inventory/lot/{id}/del/',
|
'/inventory/lot/{id}/del/',
|
||||||
'/inventory/lot/{lot_id}/device/',
|
'/inventory/lot/{lot_id}/device/',
|
||||||
'/inventory/lot/{lot_id}/device/add/',
|
'/inventory/lot/{lot_id}/device/add/',
|
||||||
|
'/inventory/lot/{lot_id}/deliverynote/',
|
||||||
|
'/inventory/lot/{lot_id}/receivernote/',
|
||||||
'/inventory/lot/{lot_id}/trade-document/add/',
|
'/inventory/lot/{lot_id}/trade-document/add/',
|
||||||
'/inventory/lot/{lot_id}/transfer/{type_id}/',
|
'/inventory/lot/{lot_id}/transfer/{type_id}/',
|
||||||
'/inventory/lot/{lot_id}/transfer/',
|
'/inventory/lot/{lot_id}/transfer/',
|
||||||
|
|
|
@ -1175,3 +1175,137 @@ def test_edit_transfer(user3: UserClientFlask):
|
||||||
assert 'one one one' in body
|
assert 'one one one' in body
|
||||||
assert '<i class="bi bi-trash"></i> Delete Lot' not in body
|
assert '<i class="bi bi-trash"></i> Delete Lot' not in body
|
||||||
assert 'Transfer (<span class="text-danger">Closed</span>)' in body
|
assert 'Transfer (<span class="text-danger">Closed</span>)' in body
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_edit_deliverynote(user3: UserClientFlask):
|
||||||
|
# create lot
|
||||||
|
user3.get('/inventory/lot/add/')
|
||||||
|
lot_name = 'lot1'
|
||||||
|
data = {
|
||||||
|
'name': lot_name,
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
}
|
||||||
|
user3.post('/inventory/lot/add/', data=data)
|
||||||
|
lot = Lot.query.filter_by(name=lot_name).one()
|
||||||
|
lot_id = lot.id
|
||||||
|
|
||||||
|
# create new incoming lot
|
||||||
|
uri = f'/inventory/lot/{lot_id}/transfer/incoming/'
|
||||||
|
data = {'csrf_token': generate_csrf(), 'code': 'AAA'}
|
||||||
|
user3.post(uri, data=data)
|
||||||
|
lot = Lot.query.filter()[1]
|
||||||
|
lot_id = lot.id
|
||||||
|
|
||||||
|
# edit delivery with errors
|
||||||
|
uri = f'/inventory/lot/{lot_id}/deliverynote/'
|
||||||
|
data = {
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
'number': 'AAA',
|
||||||
|
'units': 10,
|
||||||
|
'weight': 50,
|
||||||
|
'date': datetime.datetime.now().date() + datetime.timedelta(15),
|
||||||
|
}
|
||||||
|
body, status = user3.post(uri, data=data)
|
||||||
|
assert status == '200 OK'
|
||||||
|
assert 'Delivery Note updated error!' in body
|
||||||
|
|
||||||
|
# # edit transfer successfully
|
||||||
|
data['date'] = datetime.datetime.now().date() - datetime.timedelta(15)
|
||||||
|
body, status = user3.post(uri, data=data)
|
||||||
|
assert status == '200 OK'
|
||||||
|
assert 'Delivery Note updated successfully!' in body
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_edit_receivernote(user3: UserClientFlask):
|
||||||
|
# create lot
|
||||||
|
user3.get('/inventory/lot/add/')
|
||||||
|
lot_name = 'lot1'
|
||||||
|
data = {
|
||||||
|
'name': lot_name,
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
}
|
||||||
|
user3.post('/inventory/lot/add/', data=data)
|
||||||
|
lot = Lot.query.filter_by(name=lot_name).one()
|
||||||
|
lot_id = lot.id
|
||||||
|
|
||||||
|
# create new incoming lot
|
||||||
|
uri = f'/inventory/lot/{lot_id}/transfer/incoming/'
|
||||||
|
data = {'csrf_token': generate_csrf(), 'code': 'AAA'}
|
||||||
|
user3.post(uri, data=data)
|
||||||
|
lot = Lot.query.filter()[1]
|
||||||
|
lot_id = lot.id
|
||||||
|
|
||||||
|
# edit delivery with errors
|
||||||
|
uri = f'/inventory/lot/{lot_id}/receivernote/'
|
||||||
|
data = {
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
'number': 'AAA',
|
||||||
|
'units': 10,
|
||||||
|
'weight': 50,
|
||||||
|
'date': datetime.datetime.now().date() + datetime.timedelta(15),
|
||||||
|
}
|
||||||
|
body, status = user3.post(uri, data=data)
|
||||||
|
assert status == '200 OK'
|
||||||
|
assert 'Receiver Note updated error!' in body
|
||||||
|
|
||||||
|
# # edit transfer successfully
|
||||||
|
data['date'] = datetime.datetime.now().date() - datetime.timedelta(15)
|
||||||
|
body, status = user3.post(uri, data=data)
|
||||||
|
assert status == '200 OK'
|
||||||
|
assert 'Receiver Note updated successfully!' in body
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.mvp
|
||||||
|
@pytest.mark.usefixtures(conftest.app_context.__name__)
|
||||||
|
def test_edit_notes_with_closed_transfer(user3: UserClientFlask):
|
||||||
|
# create lot
|
||||||
|
user3.get('/inventory/lot/add/')
|
||||||
|
lot_name = 'lot1'
|
||||||
|
data = {
|
||||||
|
'name': lot_name,
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
}
|
||||||
|
user3.post('/inventory/lot/add/', data=data)
|
||||||
|
lot = Lot.query.filter_by(name=lot_name).one()
|
||||||
|
lot_id = lot.id
|
||||||
|
|
||||||
|
# create new incoming lot
|
||||||
|
uri = f'/inventory/lot/{lot_id}/transfer/incoming/'
|
||||||
|
data = {'csrf_token': generate_csrf(), 'code': 'AAA'}
|
||||||
|
user3.post(uri, data=data)
|
||||||
|
lot = Lot.query.filter()[1]
|
||||||
|
lot_id = lot.id
|
||||||
|
|
||||||
|
# edit transfer adding date
|
||||||
|
uri = f'/inventory/lot/{lot_id}/transfer/'
|
||||||
|
data['date'] = datetime.datetime.now().date() - datetime.timedelta(15)
|
||||||
|
user3.post(uri, data=data)
|
||||||
|
assert lot.transfer.closed is True
|
||||||
|
|
||||||
|
# edit delivery with errors
|
||||||
|
uri = f'/inventory/lot/{lot_id}/deliverynote/'
|
||||||
|
data = {
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
'number': 'AAA',
|
||||||
|
'units': 10,
|
||||||
|
'weight': 50,
|
||||||
|
}
|
||||||
|
body, status = user3.post(uri, data=data)
|
||||||
|
assert status == '200 OK'
|
||||||
|
assert 'Delivery Note updated error!' in body
|
||||||
|
|
||||||
|
# edit receiver with errors
|
||||||
|
uri = f'/inventory/lot/{lot_id}/receivernote/'
|
||||||
|
data = {
|
||||||
|
'csrf_token': generate_csrf(),
|
||||||
|
'number': 'AAA',
|
||||||
|
'units': 10,
|
||||||
|
'weight': 50,
|
||||||
|
}
|
||||||
|
body, status = user3.post(uri, data=data)
|
||||||
|
assert status == '200 OK'
|
||||||
|
assert 'Receiver Note updated error!' in body
|
||||||
|
|
Reference in New Issue