Ejemplo n.º 1
0
class ProductInventoryView(ModelViewWithAccess):
    from formatter import supplier_formatter, product_formatter, available_quantity_formatter, default_decimal_formatter
    can_edit = False
    can_delete = False
    can_create = False
    can_view_details = False

    @property
    def role_identify(self):
        return "product_inventory"

    column_list = ('name', 'available_quantity', 'in_transit_quantity',
                   'average_purchase_price', 'average_retail_price',
                   'average_unit_profit', 'weekly_sold_qty',
                   'weekly_average_profit', 'inventory_advice')

    column_searchable_list = ('name', 'supplier.name', 'mnemonic',
                              'supplier.mnemonic')

    column_filters = (
        FloatSmallerFilter(Product.available_quantity,
                           lazy_gettext('Available Quantity')),
        FloatGreaterFilter(Product.available_quantity,
                           lazy_gettext('Available Quantity')),
        FloatGreaterFilter(Product.in_transit_quantity,
                           lazy_gettext('In Transit Quantity')),
    )

    column_sortable_list = (
        'name',
        'available_quantity',
        'in_transit_quantity',
        'average_purchase_price',
        'average_retail_price',
        'average_unit_profit',
    )

    # column_filters = column_searchable_list
    column_labels = {
        'supplier.name': lazy_gettext('Supplier Name'),
        'supplier': lazy_gettext('Supplier'),
        'name': lazy_gettext('Product Name'),
        'available_quantity': lazy_gettext('Available Quantity'),
        'in_transit_quantity': lazy_gettext('In Transit Quantity'),
        'average_purchase_price': lazy_gettext('Average purchase price'),
        'average_retail_price': lazy_gettext('Average retail price'),
        'average_unit_profit': lazy_gettext('Average unit profit'),
        'weekly_sold_qty': lazy_gettext('Weekly sold quantity'),
        'weekly_average_profit': lazy_gettext('Weekly average profit'),
        'inventory_advice': lazy_gettext('Management advice')
    }

    column_formatters = {
        'supplier': supplier_formatter,
        'name': product_formatter,
        'available_quantity': available_quantity_formatter,
        'average_purchase_price': default_decimal_formatter,
        'average_retail_price': default_decimal_formatter,
        'average_unit_profit': default_decimal_formatter,
        'weekly_sold_qty': default_decimal_formatter,
        'weekly_average_profit': default_decimal_formatter
    }

    def get_query(self):
        self.update_need_advice_flag()
        return super(ProductInventoryView,
                     self).get_query().filter(self.model.need_advice == True)

    def update_need_advice_flag(self):
        def get_time_in_second(key):
            return int(time.time())

        from psi.app.service import Info
        from flask import current_app
        last_update = Info.get('need_advice_last_update_timestamp',
                               get_time_in_second)
        if int(
            (time.time() -
             last_update)) > current_app.config['NEED_ADVICE_UPDATE_SECONDS']:
            session = Info.get_db().session
            all_products = Product.query.all()
            for p in all_products:
                p.need_advice = False
                session.add(p)
            products = Product.query.order_by(
                Product.weekly_average_profit).limit(50).all()
            for p in products:
                p.need_advice = True
                session.add(p)
            session.commit()

    def get_count_query(self):
        return super(
            ProductInventoryView,
            self).get_count_query().filter(self.model.need_advice == True)
Ejemplo n.º 2
0
class ReceivingAdmin(ModelViewWithAccess, DeleteValidator,
                     ModelWithLineFormatter):
    from .formatter import supplier_formatter, purchase_order_formatter, \
        inventory_transaction_formatter, default_date_formatter
    from psi.app.models import ReceivingLine, Receiving, PurchaseOrder

    inline_models = (ReceivingLineInlineAdmin(ReceivingLine), )
    column_list = ('id', 'purchase_order', 'supplier', 'date', 'status',
                   'total_amount', 'inventory_transaction', 'remark')
    form_excluded_columns = ('inventory_transaction', )
    form_columns = ('purchase_order', 'transient_po', 'status', 'date',
                    'total_amount', 'remark', 'lines', 'create_lines')

    column_editable_list = ('remark', )
    form_create_rules = (
        'purchase_order',
        'status',
        'date',
        'remark',
        'create_lines',
    )
    form_edit_rules = (
        'transient_po',
        'status',
        'date',
        'remark',
        'lines',
    )

    form_extra_fields = {
        'create_lines':
        BooleanField(
            label=lazy_gettext('Create Lines for unreceived products'),
            description=lazy_gettext(
                'Create receiving lines based on '
                'not yet received products in the purchase order')),
        'transient_po':
        DisabledStringField(label=lazy_gettext('Related Purchase Order')),
        "total_amount":
        DisabledStringField(label=lazy_gettext('Total Amount')),
    }

    column_details_list = (
        'id',
        'supplier',
        'date',
        'status',
        'total_amount',
        'remark',
        'lines',
        'purchase_order',
        'inventory_transaction',
    )
    form_widget_args = {
        'create_lines': {
            'default': True
        },
    }
    column_sortable_list = ('id', ('supplier', 'id'), ('purchase_order', 'id'),
                            ('status',
                             'status.display'), 'date', 'total_amount')

    column_filters = ('date', 'status.display', 'purchase_order.supplier.name',
                      FloatSmallerFilter(Receiving.total_amount,
                                         lazy_gettext('Total Amount')),
                      FloatGreaterFilter(Receiving.total_amount,
                                         lazy_gettext('Total Amount')),
                      FloatEqualFilter(Receiving.total_amount,
                                       lazy_gettext('Total Amount')))

    column_searchable_list = ('status.display', 'purchase_order.supplier.name',
                              'remark')
    column_labels = {
        'id': lazy_gettext('id'),
        'purchase_order': lazy_gettext('Related Purchase Order'),
        'supplier': lazy_gettext('Supplier'),
        'status': lazy_gettext('Status'),
        'date': lazy_gettext('Date'),
        'remark': lazy_gettext('Remark'),
        'total_amount': lazy_gettext('Total Amount'),
        'inventory_transaction': lazy_gettext('Inventory Transaction'),
        'lines': lazy_gettext('Lines'),
        'status.display': lazy_gettext('Status'),
        'purchase_order.supplier.name': lazy_gettext('Supplier Name')
    }
    form_args = dict(
        status=dict(query_factory=Receiving.status_filter,
                    description=lazy_gettext(
                        'Current status of the receiving document, '
                        'completed receiving can not be updated or deleted')),
        purchase_order=dict(description=lazy_gettext(
            'Please select a purchase order and save the form, then add receiving lines accordingly'
        ),
                            query_factory=partial(
                                PurchaseOrder.status_filter, (
                                    const.PO_ISSUED_STATUS_KEY,
                                    const.PO_PART_RECEIVED_STATUS_KEY,
                                ))),
        date=dict(default=datetime.now()),
    )

    @property
    def line_fields(self):
        if not security_util.user_has_role('purchase_price_view'):
            return [product_field, quantity_field]
        else:
            return [
                product_field, quantity_field, price_field, total_amount_field
            ]

    column_formatters = {
        'supplier': supplier_formatter,
        'purchase_order': purchase_order_formatter,
        'inventory_transaction': inventory_transaction_formatter,
        'date': default_date_formatter,
        'lines': line_formatter
    }

    def on_model_delete(self, model):
        super(ReceivingAdmin, self).on_model_delete(model)
        DeleteValidator.validate_status_for_change(
            model, const.RECEIVING_COMPLETE_STATUS_KEY,
            gettext(
                'Receiving document can not be update nor delete on complete status'
            ))

    def on_model_change(self, form, model, is_created):
        from psi.app.models import PurchaseOrder
        super(ReceivingAdmin, self).on_model_change(form, model, is_created)
        if is_created:
            available_info = model.purchase_order.get_available_lines_info()
            # 4. Check any qty available for receiving?
            if PurchaseOrder.all_lines_received(available_info):
                raise ValidationError(
                    gettext('There\'s no unreceived items in this PO.'))
            # 5. Create receiving lines based on the calculated result.
            if model.create_lines:
                model.lines = PurchaseOrder.create_receiving_lines(
                    available_info)
        model.operate_inv_trans_by_recv_status()
        model.update_purchase_order_status()

    def create_form(self, obj=None):
        from psi.app.models import EnumValues
        form = super(ReceivingAdmin, self).create_form(obj)
        form.status.query = [
            EnumValues.get(const.RECEIVING_DRAFT_STATUS_KEY),
        ]
        form.create_lines.data = True
        return form

    def edit_form(self, obj=None):
        from psi.app.models import PurchaseOrderLine, EnumValues
        form = super(ReceivingAdmin, self).edit_form(obj)
        po_id = obj.transient_po.id
        # Set query_factory for newly added line
        form.lines.form.purchase_order_line.kwargs['query_factory'] = partial(
            PurchaseOrderLine.header_filter, po_id)
        if obj is not None and obj.status is not None and obj.status.code == const.RECEIVING_COMPLETE_STATUS_KEY:
            form.status.query = [
                EnumValues.get(const.RECEIVING_COMPLETE_STATUS_KEY),
            ]
        # Set query option for old lines
        line_entries = form.lines.entries
        po_lines = PurchaseOrderLine.header_filter(po_id).all()
        if not security_util.user_has_role('purchase_price_view'):
            form_util.del_form_field(self, form, 'total_amount')
            form_util.del_inline_form_field(form.lines.form,
                                            form.lines.entries,
                                            'transient_price')
            form_util.del_inline_form_field(form.lines.form,
                                            form.lines.entries, 'total_amount')
        for sub_line in line_entries:
            sub_line.form.purchase_order_line.query = po_lines
        return form

    def get_list_columns(self):
        """
        This method is called instantly in list.html
        List of columns is decided runtime during render of the table
        Not decided during flask-admin blueprint startup.
        """
        columns = super(ReceivingAdmin, self).get_list_columns()
        cols = ['total_amount']
        columns = security_util.filter_columns_by_role(columns, cols,
                                                       'purchase_price_view')
        return columns

    def get_details_columns(self):
        cols = ['total_amount']
        columns = super(ReceivingAdmin, self).get_details_columns()
        columns = security_util.filter_columns_by_role(columns, cols,
                                                       'purchase_price_view')
        return columns
Ejemplo n.º 3
0
class SalesOrderAdmin(ModelViewWithAccess, ModelWithLineFormatter):
    from psi.app.models import SalesOrderLine, SalesOrder
    from formatter import expenses_formatter, incoming_formatter, \
        shipping_formatter, default_date_formatter, line_formatter

    line_fields = [product_field, quantity_field, retail_price_field,
                   actual_amount_field, original_amount_field,
                   discount_amount_field, remark_field]

    column_extra_row_actions = [
        MarkShipRowAction('fa fa-camera-retro'),
        MarkInvalidRowAction('fa fa-minus-circle')
    ]

    column_list = ('id', 'type', 'status', 'customer', 'logistic_amount', 'actual_amount', 'original_amount',
                   'discount_amount', 'order_date', 'incoming', 'expense', 'so_shipping', 'remark')
    column_filters = ('order_date', 'logistic_amount',
                      FloatSmallerFilter(SalesOrder.actual_amount, lazy_gettext('Actual Amount')),
                      FloatGreaterFilter(SalesOrder.actual_amount, lazy_gettext('Actual Amount')),
                      FloatEqualFilter(SalesOrder.actual_amount, lazy_gettext('Actual Amount')),
                      FloatSmallerFilter(SalesOrder.discount_amount, lazy_gettext('Discount Amount')),
                      FloatGreaterFilter(SalesOrder.discount_amount, lazy_gettext('Discount Amount')),
                      FloatEqualFilter(SalesOrder.discount_amount, lazy_gettext('Discount Amount')),
                      FloatSmallerFilter(SalesOrder.original_amount, lazy_gettext('Total Amount')),
                      FloatGreaterFilter(SalesOrder.original_amount, lazy_gettext('Total Amount')),
                      FloatEqualFilter(SalesOrder.original_amount, lazy_gettext('Total Amount')),)

    column_searchable_list = ('customer.first_name', 'customer.last_name', 'remark', 'type.display', 'type.code',
                              'status.display', 'status.code', 'customer.mobile_phone', 'customer.email',
                              'customer.address', 'customer.level.display', 'customer.join_channel.display')

    form_columns = ('id', 'customer', 'logistic_amount', 'status', 'order_date', 'remark', 'actual_amount',
                    'original_amount', 'discount_amount', 'lines')
    form_edit_rules = ('customer', 'logistic_amount', 'order_date', 'status', 'remark', 'actual_amount',
                       'original_amount', 'discount_amount', 'lines')
    form_create_rules = ('customer', 'logistic_amount',  'order_date', 'status', 'remark', 'lines',)

    column_details_list = ('id', 'type', 'status', 'customer', 'external_id', 'logistic_amount', 'order_date', 'remark',
                           'actual_amount', 'original_amount', 'discount_amount', 'incoming', 'expense',
                           'so_shipping', 'lines',)

    column_editable_list = ('remark',)

    form_extra_fields = {
        'transient_external_id': DisabledStringField(label=lazy_gettext('External Id')),
        'actual_amount': DisabledStringField(label=lazy_gettext('Actual Amount')),
        'original_amount': DisabledStringField(label=lazy_gettext('Original Amount')),
        'discount_amount': DisabledStringField(label=lazy_gettext('Discount Amount'))
    }

    form_overrides = dict(external_id=ReadonlyStringField)

    form_args = dict(
        logistic_amount=dict(default=0),
        order_date=dict(default=datetime.now()),
        status=dict(query_factory=SalesOrder.status_option_filter),
        customer=dict(description=lazy_gettext('Customer can be searched by name, mobile phone, email or first letter of his/her name'))
    )

    form_excluded_columns = ('incoming', 'expense', 'so_shipping')
    column_sortable_list = ('id', 'logistic_amount', 'actual_amount', 'original_amount', 'discount_amount',
                            'order_date',('status', 'status.display'), ('type', 'type.display'))

    form_ajax_refs = {
        'customer': QueryAjaxModelLoader('customer',
                                         service.Info.get_db().session, Customer,
                                         filters=[],
                                         fields=['first_name', 'last_name', 'mobile_phone', 'email', 'mnemonic']),
        'product': QueryAjaxModelLoader(name='product',
                                        session=service.Info.get_db().session,
                                        model=Product,
                                        # --> Still need to filter the products by organization.
                                        # --> Line 209 is commented out, need to bring it back.
                                        fields=['name', 'mnemonic'])
    }

    inline_models = (SalesOrderLineInlineAdmin(SalesOrderLine),)

    column_formatters = {
        'expense': expenses_formatter,
        'incoming': incoming_formatter,
        'so_shipping': shipping_formatter,
        'order_date': default_date_formatter,
        'lines': line_formatter,
    }

    column_labels = {
        'id': lazy_gettext('id'),
        'logistic_amount': lazy_gettext('Logistic Amount'),
        'order_date': lazy_gettext('Order Date'),
        'remark': lazy_gettext('Remark'),
        'actual_amount': lazy_gettext('Actual Amount'),
        'original_amount': lazy_gettext('Original Amount'),
        'discount_amount': lazy_gettext('Discount Amount'),
        'incoming': lazy_gettext('Related Incoming'),
        'expense': lazy_gettext('Related Expense'),
        'so_shipping': lazy_gettext('Related Shipping'),
        'lines': lazy_gettext('Lines'),
        'external_id': lazy_gettext('External Id'),
        'customer': lazy_gettext('Customer'),
        'customer.name': lazy_gettext('Customer'),
        'status': lazy_gettext('Status'),
        'type': lazy_gettext('Type'),
    }

    def create_form(self, obj=None):
        form = super(SalesOrderAdmin, self).create_form(obj)
        self.hide_line_derive_fields_on_create_form(form)
        form_util.filter_by_organization(form.customer, Customer)
        self.filter_product_by_organization(form)
        return form

    def hide_line_derive_fields_on_create_form(self, form):
        form.lines.form.actual_amount = None
        form.lines.form.discount_amount = None
        form.lines.form.original_amount = None
        form.lines.form.price_discount = None
        form.lines.form.retail_price = None

    def edit_form(self, obj=None):
        form = super(SalesOrderAdmin, self).edit_form(obj)
        form_util.filter_by_organization(form.customer, Customer)
        self.filter_product_by_organization(form)
        return form

    @staticmethod
    def filter_product_by_organization(form):
        # Set query factory for new created line
        # TODO.xqliu Fix this for AJAX lookup
        # If we uncomment follow line to limit the query to current organization
        # The AJAX look up fails.
        # form.lines.form.product.kwargs['query_factory'] = partial(Product.organization_filter, current_user.organization_id)
        # Set query object filter for existing lines
        line_entries = form.lines.entries
        for sub_line in line_entries:
            form_util.filter_by_organization(sub_line.form.product, Product)

    def on_model_change(self, form, model, is_created):
        super(SalesOrderAdmin, self).on_model_change(form, model, is_created)
        if is_created:
            model.type = EnumValues.get(const.DIRECT_SO_TYPE_KEY)
            if model.status is None:
                model.status = EnumValues.get(const.SO_DELIVERED_STATUS_KEY)
            model.organization = current_user.organization
        if model.status.code == const.SO_DELIVERED_STATUS_KEY:
            incoming = SalesOrderService.create_or_update_incoming(model)
            expense = SalesOrderService.create_or_update_expense(model)
            shipping = None
            if model.type.code == const.DIRECT_SO_TYPE_KEY:
                shipping = SalesOrderService.create_or_update_shipping(model)
            db = service.Info.get_db()
            if expense is not None:
                db.session.add(expense)
            if incoming is not None:
                db.session.add(incoming)
            if shipping is not None:
                db.session.add(shipping)

    @property
    def role_identify(self):
        return "direct_sales_order"
Ejemplo n.º 4
0
class BasePurchaseOrderAdmin(ModelViewWithAccess, DeleteValidator, ModelWithLineFormatter):
    from psi.app.models import PurchaseOrderLine
    from psi.app.views.formatter import supplier_formatter, expenses_formatter, \
        receivings_formatter, default_date_formatter, line_formatter

    column_labels = {
        'id': lazy_gettext('id'),
        'logistic_amount': lazy_gettext('Logistic Amount'),
        'order_date': lazy_gettext('Order Date'),
        'to_organization': lazy_gettext('Supply Organization'),
        'supplier': lazy_gettext('Supplier'),
        'remark': lazy_gettext('Remark'),
        'status': lazy_gettext('Status'),
        'all_expenses': lazy_gettext('Related Expense'),
        'all_receivings': lazy_gettext('Related Receiving'),
        'total_amount': lazy_gettext('Total Amount'),
        'goods_amount': lazy_gettext('Goods Amount'),
        'lines': lazy_gettext('Lines'),
        'supplier.name': lazy_gettext('Supplier Name'),
        'status.display': lazy_gettext('Status')
    }

    @property
    def line_fields(self):
        if not security_util.user_has_role('purchase_price_view'):
            return [product_field, quantity_field, remark_field]
        else:
            return [product_field, quantity_field, unit_price_field,
                    total_amount_field, remark_field]

    column_formatters = {
        'supplier': supplier_formatter,
        'all_expenses': expenses_formatter,
        'all_receivings': receivings_formatter,
        'order_date': default_date_formatter,
        "lines": line_formatter
    }

    form_args = dict(
        status=dict(query_factory=PurchaseOrder.status_option_filter,
                    description=lazy_gettext(
                        'Purchase order can only be created in draft status, '
                        'and partially '
                        'received & received status are driven by associated '
                        'receiving document')),
        supplier=dict(description=lazy_gettext(
            'Please select a supplier and save the form, '
            'then add purchase order lines accordingly')),
        logistic_amount=dict(default=0),
        order_date=dict(default=datetime.now()),
    )

    column_filters = ('order_date', 'logistic_amount', 'status.display',
                      FloatSmallerFilter(PurchaseOrder.goods_amount,
                                         lazy_gettext('Goods Amount')),
                      FloatGreaterFilter(PurchaseOrder.goods_amount,
                                         lazy_gettext('Goods Amount')),
                      FloatEqualFilter(PurchaseOrder.goods_amount,
                                       lazy_gettext('Goods Amount')))

    form_excluded_columns = ('expenses', 'receivings')
    column_editable_list = ('remark',)

    """Type code of the purchase orders should be displayed in the subview."""
    type_code = None

    def get_list_columns(self):
        """
        This method is called instantly in list.html
        List of columns is decided runtime during render of the table
        Not decided during flask-admin blueprint startup.
        """
        columns = super(BasePurchaseOrderAdmin, self).get_list_columns()
        cols = ['goods_amount', 'total_amount', 'all_expenses']
        columns = security_util.filter_columns_by_role(
            columns, cols,'purchase_price_view'
        )
        columns = self.filter_columns_by_organization_type(columns)
        return columns

    def filter_columns_by_organization_type(self, columns):
        new_col_list = []
        local_user = current_user._get_current_object()
        if local_user is not None:
            if local_user.organization.type.code == "FRANCHISE_STORE":
                for col in columns:
                    if col[0] != 'supplier':
                        new_col_list.append(col)
            elif local_user.organization.type.code == "DIRECT_SELLING_STORE":
                for col in columns:
                    if col[0] != 'to_organization':
                        new_col_list.append(col)
            return tuple(new_col_list)
        else:
            return columns

    def get_details_columns(self):
        cols = ['goods_amount', 'total_amount', 'all_expenses']
        columns = super(BasePurchaseOrderAdmin, self).get_details_columns()
        columns = security_util.filter_columns_by_role(
            columns, cols,'purchase_price_view'
        )
        columns = self.filter_columns_by_organization_type(columns)
        return columns

    def on_model_change(self, form, model, is_created):
        super(BasePurchaseOrderAdmin, self).on_model_change(form, model, is_created)
        if not security_util.user_has_role('purchase_price_view'):
            for l in model.lines:
                l.unit_price = l.product.purchase_price
        DeleteValidator.validate_status_for_change(
            model, const.PO_RECEIVED_STATUS_KEY,
            gettext('Purchase order can not be update nor delete on received status')
        )
        if is_created:
            model.type = EnumValues.get(self.type_code)
        PurchaseOrderService.create_expense_receiving(model)

    def get_query(self):
        po_type = EnumValues.get(self.type_code)
        return super(BasePurchaseOrderAdmin, self).get_query().filter(self.model.type == po_type)

    def get_count_query(self):
        po_type = EnumValues.get(self.type_code)
        return super(BasePurchaseOrderAdmin, self).get_count_query().filter(self.model.type == po_type)

    def on_model_delete(self, model):
        DeleteValidator.validate_status_for_change(
            model,const.PO_RECEIVED_STATUS_KEY,
            gettext('Purchase order can not be update nor delete on received status'))
        DeleteValidator.validate_status_for_change(
            model,const.PO_ISSUED_STATUS_KEY,
            gettext('Purchase order can not be update nor delete on issued status'))

    inline_models = (PurchaseOrderLineInlineAdmin(PurchaseOrderLine),)

    def after_model_change(self, form, model, is_created):
        pass
Ejemplo n.º 5
0
class InventoryTransactionAdmin(ModelViewWithAccess, ModelWithLineFormatter):
    can_delete = False

    column_list = ('id', 'type', 'date', 'total_amount', 'it_receiving', 'it_shipping', 'remark')
    column_sortable_list = ('id', ('type', 'type.display'), 'total_amount', 'date',)
    form_columns = ('type', 'date', 'total_amount', 'remark', 'lines')
    form_create_rules = ('type', 'date', 'remark', 'lines',)
    form_edit_rules = ('type', 'date', 'remark', 'lines',)

    column_editable_list = ('remark',)

    column_filters = ('date',
                      FloatGreaterFilter(InventoryTransaction.total_amount, lazy_gettext('Total Amount')),
                      FloatSmallerFilter(InventoryTransaction.total_amount, lazy_gettext('Total Amount')),)
    column_searchable_list = ('type.display', 'remark')

    column_details_list = ('id', 'type', 'date', 'total_amount', 'remark', 'lines', 'it_receiving', 'it_shipping',)

    column_labels = {
        'id': lazy_gettext('id'),
        'type': lazy_gettext('Inventory Transaction Type'),
        'date': lazy_gettext('Date'),
        'total_amount': lazy_gettext('Total Amount'),
        'remark': lazy_gettext('Remark'),
        'lines': lazy_gettext('Lines'),
        'it_receiving': lazy_gettext('Related Receiving'),
        'it_shipping': lazy_gettext('Related Shipping'),
    }

    form_excluded_columns = ('it_shipping', 'it_receiving')

    form_args = dict(
        type=dict(query_factory=InventoryTransaction.manual_type_filter),
        date=dict(default=datetime.now()),
    )

    from psi.app.views.components import DisabledStringField

    form_extra_fields = {
        'total_amount': DisabledStringField(label=lazy_gettext('Total Amount')),
    }

    form_ajax_refs = {
        'product': QueryAjaxModelLoader(name='product',
                                        session=service.Info.get_db().session,
                                        model=Product,
                                        # --> Still need to filter the products by organization.
                                        # --> Line 209 is commented out, need to bring it back.
                                        fields=['name', 'mnemonic'])
    }

    column_formatters = {
        'it_receiving': receivings_formatter,
        'it_shipping': shipping_formatter,
        'date': default_date_formatter,
        'lines': line_formatter,
    }

    inline_models = (InventoryTransactionLineInlineAdmin(InventoryTransactionLine),)

    def get_list_columns(self):
        """
        This method is called instantly in list.html
        List of columns is decided runtime during render of the table
        Not decided during flask-admin blueprint startup.
        """
        columns = super(InventoryTransactionAdmin, self).get_list_columns()
        cols = ['total_amount']
        columns = security_util.filter_columns_by_role(
            columns, cols, 'purchase_price_view'
        )
        return columns

    def get_details_columns(self):
        cols = ['total_amount']
        columns = super(InventoryTransactionAdmin, self).get_details_columns()
        columns = security_util.filter_columns_by_role(
            columns, cols, 'purchase_price_view'
        )
        return columns

    @property
    def line_fields(self):
        if not security_util.user_has_role('purchase_price_view'):
            return [type_field, date_field, product_field, quantity_field,
                    saleable_quantity_field, remark_field]
        return [type_field, date_field, product_field, price_field,
                quantity_field, total_amount_field, saleable_quantity_field,
                remark_field]
Ejemplo n.º 6
0
class ShippingAdmin(ModelViewWithAccess, ModelWithLineFormatter):

    from formatter import inventory_transaction_formatter, sales_order_formatter, default_date_formatter
    from psi.app.models import ShippingLine, Shipping

    inline_models = (ShippingLineInlineAdmin(ShippingLine), )
    column_list = ('id', 'status', 'date', 'total_amount', 'sales_order',
                   'inventory_transaction', 'remark')

    can_edit = False
    can_create = False
    can_delete = False

    form_columns = ('sales_order', 'status', 'date', 'total_amount', 'remark',
                    'lines')
    form_extra_fields = {
        "total_amount":
        DisabledStringField(label=lazy_gettext('Total Amount')),
    }

    column_filters = (
        'date',
        FloatSmallerFilter(Shipping.total_amount,
                           lazy_gettext('Total Amount')),
        FloatGreaterFilter(Shipping.total_amount,
                           lazy_gettext('Total Amount')),
        FloatEqualFilter(Shipping.total_amount, lazy_gettext('Total Amount')),
    )
    column_searchable_list = ('status.display', 'remark')

    @property
    def column_details_list(self):
        if not security_util.user_has_role('purchase_price_view'):
            return ('id', 'sales_order', 'status', 'date', 'remark', 'lines',
                    'inventory_transaction')
        return ('id', 'sales_order', 'status', 'date', 'total_amount',
                'remark', 'lines', 'inventory_transaction')

    @property
    def line_fields(self):
        # TODO.xqliu Move the judgetment stragegy to a separate method to avoid repeat myself!!!
        if not security_util.user_has_role('purchase_price_view'):
            return [product_field, quantity_field]
        return [
            product_field, price_field, quantity_field, price_field,
            total_amount_field
        ]

    column_sortable_list = ('id', ('sales_order',
                                   'id'), ('status', 'status.display'), 'date',
                            'total_amount')
    column_labels = {
        'id': lazy_gettext('id'),
        'sales_order': lazy_gettext('Related Sales Order'),
        'status': lazy_gettext('Status'),
        'date': lazy_gettext('Date'),
        'remark': lazy_gettext('Remark'),
        'total_amount': lazy_gettext('Total Amount'),
        'inventory_transaction': lazy_gettext('Inventory Transaction'),
        'lines': lazy_gettext('Lines'),
        'status.display': lazy_gettext('Status'),
    }

    form_args = dict(
        status=dict(query_factory=Shipping.status_filter, ),
        date=dict(default=datetime.now()),
        lines=dict(description=lazy_gettext(
            'Modify shipping document directly is not allowed, '
            'please modify the related sales order and this shipping document '
            'will be changed accordingly')),
    )

    column_formatters = {
        'inventory_transaction': inventory_transaction_formatter,
        'sales_order': sales_order_formatter,
        'date': default_date_formatter,
        'lines': line_formatter
    }

    def on_model_change(self, form, model, is_created):
        raise ValidationError(
            gettext(
                'Modify shipping document directly is not allowed, '
                'please modify the related sales order and this shipping document '
                'will be changed accordingly'))