示例#1
0
    def test_populate(self):  # test get_compatible_ones() too
        get_ct = ContentType.objects.get_for_model
        ct = get_ct(Opportunity)
        relation_types = RelationType.objects.compatible(ct).in_bulk()

        Product = products.get_product_model()
        Service = products.get_service_model()

        self.assertNotIn(constants.REL_SUB_TARGETS, relation_types)
        self.get_relationtype_or_fail(constants.REL_SUB_TARGETS, [Opportunity],
                                      [Contact, Organisation])

        self.assertNotIn(constants.REL_SUB_EMIT_ORGA, relation_types)
        self.get_relationtype_or_fail(constants.REL_OBJ_EMIT_ORGA,
                                      [Opportunity], [Organisation])

        self.assertIn(constants.REL_OBJ_LINKED_PRODUCT, relation_types)
        self.assertNotIn(constants.REL_SUB_LINKED_PRODUCT, relation_types)
        self.get_relationtype_or_fail(constants.REL_OBJ_LINKED_PRODUCT,
                                      [Opportunity], [Product])

        self.assertIn(constants.REL_OBJ_LINKED_SERVICE, relation_types)
        self.assertNotIn(constants.REL_SUB_LINKED_SERVICE, relation_types)
        self.get_relationtype_or_fail(constants.REL_OBJ_LINKED_SERVICE,
                                      [Opportunity], [Service])

        self.assertIn(constants.REL_OBJ_LINKED_CONTACT, relation_types)
        self.assertNotIn(constants.REL_SUB_LINKED_CONTACT, relation_types)
        self.get_relationtype_or_fail(constants.REL_OBJ_LINKED_CONTACT,
                                      [Opportunity], [Contact])

        self.assertIn(constants.REL_OBJ_RESPONSIBLE, relation_types)
        self.assertNotIn(constants.REL_SUB_RESPONSIBLE, relation_types)
        self.get_relationtype_or_fail(constants.REL_OBJ_RESPONSIBLE,
                                      [Opportunity], [Contact])

        self.assertTrue(SalesPhase.objects.exists())
        self.assertTrue(Origin.objects.exists())

        # self.assertEqual(1, SettingValue.objects.filter(key_id=constants.SETTING_USE_CURRENT_QUOTE).count())
        def assertSVEqual(key, value):
            with self.assertNoException():
                sv = SettingValue.objects.get_4_key(key)

            self.assertIs(sv.value, value)

        assertSVEqual(setting_keys.quote_key, False)
        assertSVEqual(setting_keys.target_constraint_key, True)
        assertSVEqual(setting_keys.emitter_constraint_key, True)

        # Contribution to activities
        rtype = self.get_object_or_fail(RelationType,
                                        pk=REL_SUB_ACTIVITY_SUBJECT)
        self.assertTrue(rtype.subject_ctypes.filter(id=ct.id).exists())
        self.assertTrue(
            rtype.subject_ctypes.filter(id=get_ct(Contact).id).exists())
        self.assertTrue(
            rtype.symmetric_type.object_ctypes.filter(id=ct.id).exists())
示例#2
0
    def get_products(self):
        warnings.warn('AbstractOpportunity.get_products() is deprecated.',
                      DeprecationWarning)

        from creme.products import get_product_model

        return get_product_model()\
                    .objects\
                    .filter(is_deleted=False,
                            relations__object_entity=self.id,
                            relations__type=constants.REL_SUB_LINKED_PRODUCT,
                           )
示例#3
0
class ProductLineMultipleAddForm(_LineMultipleAddForm):
    items = MultiCreatorEntityField(label=_('Products'),
                                    model=products.get_product_model())

    blocks = core_forms.FieldBlockManager(
        ('general', _('Products choice'), ['items']),
        ('additional',
         _('Optional global information applied to your selected products'),
         ['quantity', 'vat', 'discount_value']))

    def _get_line_class(self):
        return ProductLine
示例#4
0
    def test_linked_products(self):
        brick_cls = bricks.LinkedProductsBrick
        brick_cls.page_size = max(5, settings.BLOCK_SIZE)

        user = self.login()
        opp01, target, emitter = self._create_opportunity_n_organisations(
            name='Opp#1')
        opp02 = Opportunity.objects.create(
            user=self.user,
            name='Opp#2',
            sales_phase=opp01.sales_phase,
            emitter=emitter,
            target=target,
        )

        sub_cat = SubCategory.objects.all()[0]
        create_product = partial(
            products.get_product_model().objects.create,
            user=user,
            unit_price=Decimal('1.23'),
            category=sub_cat.category,
            sub_category=sub_cat,
        )
        product01 = create_product(name='Eva00')
        product02 = create_product(name='Eva01')
        product03 = create_product(name='Eva02')
        product04 = create_product(name='Eva03')
        product05 = create_product(name='Eva04', is_deleted=True)

        create_rel = partial(Relation.objects.create,
                             user=user,
                             type_id=constants.REL_OBJ_LINKED_PRODUCT)
        create_rel(subject_entity=opp01, object_entity=product01)
        create_rel(subject_entity=opp01, object_entity=product02)
        create_rel(subject_entity=opp02, object_entity=product04)
        create_rel(subject_entity=opp01, object_entity=product05)

        response = self.assertGET200(opp01.get_absolute_url())
        tree = self.get_html_tree(response.content)
        brick_node = self.get_brick_node(tree, brick_cls.id_)
        self.assertInstanceLink(brick_node, product01)
        self.assertInstanceLink(brick_node, product02)
        self.assertNoInstanceLink(brick_node, product03)
        self.assertNoInstanceLink(brick_node, product04)
        self.assertNoInstanceLink(brick_node, product05)
示例#5
0
    def post(self, request, *args, **kwargs):
        klass = self.get_ctype().model_class()

        try:
            rtype_id, set_as_current, workflow_action = self.behaviours[klass]
        except KeyError as e:
            raise Http404('Bad billing document type') from e

        user = request.user
        user.has_perm_to_create_or_die(klass)
        # TODO: check in template too (must upgrade 'has_perm' to use owner!=None)
        user.has_perm_to_link_or_die(klass, owner=user)

        opp = self.get_related_entity()

        b_document = klass.objects.create(
            user=user,
            issuing_date=now(),
            status_id=1,
            currency=opp.currency,
            source=opp.emitter,
            target=opp.target,
        )

        create_relation = partial(
            Relation.objects.create,
            subject_entity=b_document,
            user=user,
        )
        create_relation(type_id=rtype_id, object_entity=opp)

        b_document.generate_number(
        )  # Need the relationship with emitter organisation
        b_document.name = self.generated_name.format(document=b_document,
                                                     opportunity=opp)
        b_document.save()

        relations = Relation.objects.filter(
            subject_entity=opp.id,
            type__in=[
                constants.REL_OBJ_LINKED_PRODUCT,
                constants.REL_OBJ_LINKED_SERVICE,
            ],
        ).select_related('object_entity')

        # TODO: Missing test case
        if relations:
            Relation.populate_real_object_entities(relations)
            vat_value = Vat.get_default_vat()
            Product = get_product_model()

            for relation in relations:
                item = relation.object_entity.get_real_entity()
                line_klass = ProductLine if isinstance(
                    item, Product) else ServiceLine
                line_klass.objects.create(
                    related_item=item,
                    related_document=b_document,
                    unit_price=item.unit_price,
                    unit=item.unit,
                    vat_value=vat_value,
                )

        if set_as_current:
            create_relation(type_id=constants.REL_SUB_CURRENT_DOC,
                            object_entity=opp)

        if workflow_action:
            workflow_action(opp.emitter, opp.target, user)

        # if request.is_ajax():
        if is_ajax(request):
            return HttpResponse()

        return redirect(opp)
示例#6
0
    def populate(self):
        already_populated = RelationType.objects.filter(
            pk=constants.REL_SUB_BILL_ISSUED, ).exists()

        Contact = persons.get_contact_model()
        Organisation = persons.get_organisation_model()
        Product = products.get_product_model()
        Service = products.get_service_model()

        # Relationships ---------------------------
        line_entities = [*lines_registry]
        create_rtype = RelationType.create
        create_rtype(
            (constants.REL_SUB_BILL_ISSUED, _('issued by'), BILLING_MODELS),
            (constants.REL_OBJ_BILL_ISSUED, _('has issued'), [Organisation]),
            is_internal=True,
            minimal_display=(False, True),
        )
        rt_sub_bill_received = create_rtype(
            (constants.REL_SUB_BILL_RECEIVED, _('received by'),
             BILLING_MODELS),
            (constants.REL_OBJ_BILL_RECEIVED, _('has received'),
             [Organisation, Contact]),
            is_internal=True,
            minimal_display=(False, True),
        )[0]
        create_rtype(
            (constants.REL_SUB_HAS_LINE, _('had the line'), BILLING_MODELS),
            (constants.REL_OBJ_HAS_LINE, _('is the line of'), line_entities),
            is_internal=True,
            minimal_display=(True, True),
        )
        create_rtype(
            (constants.REL_SUB_LINE_RELATED_ITEM, _('has the related item'),
             line_entities),
            (constants.REL_OBJ_LINE_RELATED_ITEM, _('is the related item of'),
             [Product, Service]),
            is_internal=True,
        )
        create_rtype(
            (
                constants.REL_SUB_CREDIT_NOTE_APPLIED,
                _('is used in the billing document'),
                [CreditNote],
            ),
            (
                constants.REL_OBJ_CREDIT_NOTE_APPLIED,
                _('uses the credit note'),
                [Quote, SalesOrder, Invoice],
            ),
            is_internal=True,
            minimal_display=(True, True),
        )

        if apps.is_installed('creme.activities'):
            logger.info(
                'Activities app is installed '
                '=> an Invoice/Quote/SalesOrder can be the subject of an Activity'
            )

            from creme.activities.constants import REL_SUB_ACTIVITY_SUBJECT

            RelationType.objects.get(
                pk=REL_SUB_ACTIVITY_SUBJECT, ).add_subject_ctypes(
                    Invoice, Quote, SalesOrder)

        # Payment Terms ---------------------------
        create_if_needed(
            PaymentTerms,
            {'pk': 1},
            name=_('Deposit'),
            description=_(r'20% deposit will be required'),
            is_custom=False,
        )

        # SalesOrder Status ---------------------------
        def create_order_status(pk, name, **kwargs):
            create_if_needed(SalesOrderStatus, {'pk': pk}, name=name, **kwargs)

        # NB: pk=1 + is_custom=False --> default status
        #     (used when a quote is converted in invoice for example)
        create_order_status(1,
                            pgettext('billing-salesorder', 'Issued'),
                            order=1,
                            is_custom=False)

        if not already_populated:
            create_order_status(2,
                                pgettext('billing-salesorder', 'Accepted'),
                                order=3)
            create_order_status(3,
                                pgettext('billing-salesorder', 'Rejected'),
                                order=4)
            create_order_status(4,
                                pgettext('billing-salesorder', 'Created'),
                                order=2)

        # Invoice Status ---------------------------
        def create_invoice_status(pk, name, **kwargs):
            create_if_needed(InvoiceStatus, {'pk': pk}, name=name, **kwargs)

        create_invoice_status(
            1,
            pgettext('billing-invoice', 'Draft'),
            order=1,
            is_custom=False,
        )  # Default status
        create_invoice_status(
            2,
            pgettext('billing-invoice', 'To be sent'),
            order=2,
            is_custom=False,
        )

        if not already_populated:
            create_invoice_status(
                3,
                pgettext('billing-invoice', 'Sent'),
                order=3,
                pending_payment=True,
            )
            create_invoice_status(
                4,
                pgettext('billing-invoice', 'Resulted'),
                order=5,
            )
            create_invoice_status(
                5,
                pgettext('billing-invoice', 'Partly resulted'),
                order=4,
                pending_payment=True,
            )
            create_invoice_status(
                6,
                _('Collection'),
                order=7,
            )
            create_invoice_status(
                7,
                _('Resulted collection'),
                order=6,
            )
            create_invoice_status(
                8,
                pgettext('billing-invoice', 'Canceled'),
                order=8,
            )

        # CreditNote Status ---------------------------
        def create_cnote_status(pk, name, **kwargs):
            create_if_needed(CreditNoteStatus, {'pk': pk}, name=name, **kwargs)

        create_cnote_status(1,
                            pgettext('billing-creditnote', 'Draft'),
                            order=1,
                            is_custom=False)

        if not already_populated:
            create_cnote_status(2,
                                pgettext('billing-creditnote', 'Issued'),
                                order=2)
            create_cnote_status(3,
                                pgettext('billing-creditnote', 'Consumed'),
                                order=3)
            create_cnote_status(4,
                                pgettext('billing-creditnote', 'Out of date'),
                                order=4)

        # ---------------------------
        EntityFilter.objects.smart_update_or_create(
            'billing-invoices_unpaid',
            name=_('Invoices unpaid'),
            model=Invoice,
            user='******',
            conditions=[
                condition_handler.RegularFieldConditionHandler.build_condition(
                    model=Invoice,
                    operator=operators.EqualsOperator,
                    field_name='status__pending_payment',
                    values=[True],
                ),
            ],
        )
        EntityFilter.objects.smart_update_or_create(
            'billing-invoices_unpaid_late',
            name=_('Invoices unpaid and late'),
            model=Invoice,
            user='******',
            conditions=[
                condition_handler.RegularFieldConditionHandler.build_condition(
                    model=Invoice,
                    operator=operators.EqualsOperator,
                    field_name='status__pending_payment',
                    values=[True],
                ),
                condition_handler.DateRegularFieldConditionHandler.
                build_condition(
                    model=Invoice,
                    field_name='expiration_date',
                    date_range='in_past',
                ),
            ],
        )
        current_year_invoice_filter = EntityFilter.objects.smart_update_or_create(
            'billing-current_year_invoices',
            name=_('Current year invoices'),
            model=Invoice,
            user='******',
            conditions=[
                condition_handler.DateRegularFieldConditionHandler.
                build_condition(
                    model=Invoice,
                    field_name='issuing_date',
                    date_range='current_year',
                ),
            ],
        )
        current_year_unpaid_invoice_filter = EntityFilter.objects.smart_update_or_create(
            'billing-current_year_unpaid_invoices',
            name=_('Current year and unpaid invoices'),
            model=Invoice,
            user='******',
            conditions=[
                condition_handler.DateRegularFieldConditionHandler.
                build_condition(
                    model=Invoice,
                    field_name='issuing_date',
                    date_range='current_year',
                ),
                condition_handler.RegularFieldConditionHandler.build_condition(
                    model=Invoice,
                    operator=operators.EqualsOperator,
                    field_name='status__pending_payment',
                    values=[True],
                ),
            ],
        )

        # ---------------------------
        def create_hf(hf_pk, name, model, status=True):
            HeaderFilter.objects.create_if_needed(
                pk=hf_pk,
                name=name,
                model=model,
                cells_desc=[
                    (EntityCellRegularField, {
                        'name': 'name'
                    }),
                    EntityCellRelation(model=model,
                                       rtype=rt_sub_bill_received),
                    (EntityCellRegularField, {
                        'name': 'number'
                    }),
                    (EntityCellRegularField, {
                        'name': 'status'
                    }) if status else None,
                    (EntityCellRegularField, {
                        'name': 'total_no_vat'
                    }),
                    (EntityCellRegularField, {
                        'name': 'issuing_date'
                    }),
                    (EntityCellRegularField, {
                        'name': 'expiration_date'
                    }),
                ],
            )

        create_hf(constants.DEFAULT_HFILTER_INVOICE, _('Invoice view'),
                  Invoice)
        create_hf(constants.DEFAULT_HFILTER_QUOTE, _('Quote view'), Quote)
        create_hf(constants.DEFAULT_HFILTER_ORDER, _('Sales order view'),
                  SalesOrder)
        create_hf(constants.DEFAULT_HFILTER_CNOTE, _('Credit note view'),
                  CreditNote)
        create_hf(
            constants.DEFAULT_HFILTER_TEMPLATE,
            _('Template view'),
            TemplateBase,
            status=False,
        )

        def create_hf_lines(hf_pk, name, model):
            build_cell = EntityCellRegularField.build
            HeaderFilter.objects.create_if_needed(
                pk=hf_pk,
                name=name,
                model=model,
                cells_desc=[
                    build_cell(model=model, name='on_the_fly_item'),
                    build_cell(model=model, name='quantity'),
                    build_cell(model=model, name='unit_price'),
                ],
            )

        create_hf_lines('billing-hg_product_lines', _('Product lines view'),
                        ProductLine)
        create_hf_lines('billing-hg_service_lines', _('Service lines view'),
                        ServiceLine)

        # ---------------------------
        creation_only_groups_desc = [
            {
                'name':
                _('Properties'),
                'cells': [
                    (
                        EntityCellCustomFormSpecial,
                        {
                            'name':
                            EntityCellCustomFormSpecial.CREME_PROPERTIES
                        },
                    ),
                ],
            },
            {
                'name':
                _('Relationships'),
                'cells': [
                    (
                        EntityCellCustomFormSpecial,
                        {
                            'name': EntityCellCustomFormSpecial.RELATIONS
                        },
                    ),
                ],
            },
        ]

        def build_custom_form_items(model, creation_descriptor,
                                    edition_descriptor, field_names):
            base_groups = [
                {
                    'name':
                    _('General information'),
                    'layout':
                    LAYOUT_DUAL_FIRST,
                    'cells': [
                        *((EntityCellRegularField, {
                            'name': fname
                        }) for fname in field_names),
                        (
                            EntityCellCustomFormSpecial,
                            {
                                'name':
                                EntityCellCustomFormSpecial.
                                REMAINING_REGULARFIELDS
                            },
                        ),
                    ],
                },
                {
                    'name':
                    _('Organisations'),
                    'layout':
                    LAYOUT_DUAL_SECOND,
                    'cells': [
                        BillingSourceSubCell(model=model).into_cell(),
                        BillingTargetSubCell(model=model).into_cell(),
                    ],
                },
                {
                    'name': _('Description'),
                    'layout': LAYOUT_DUAL_SECOND,
                    'cells': [
                        (EntityCellRegularField, {
                            'name': 'description'
                        }),
                    ],
                },
                {
                    'name':
                    _('Custom fields'),
                    'layout':
                    LAYOUT_DUAL_SECOND,
                    'cells': [
                        (
                            EntityCellCustomFormSpecial,
                            {
                                'name':
                                EntityCellCustomFormSpecial.
                                REMAINING_CUSTOMFIELDS
                            },
                        ),
                    ],
                },
            ]

            CustomFormConfigItem.objects.create_if_needed(
                descriptor=creation_descriptor,
                groups_desc=[
                    *base_groups,
                    *creation_only_groups_desc,
                ],
            )
            CustomFormConfigItem.objects.create_if_needed(
                descriptor=edition_descriptor,
                groups_desc=base_groups,
            )

        build_custom_form_items(
            model=Invoice,
            creation_descriptor=custom_forms.INVOICE_CREATION_CFORM,
            edition_descriptor=custom_forms.INVOICE_EDITION_CFORM,
            field_names=[
                'user',
                'name',
                'number',
                'status',
                'issuing_date',
                'expiration_date',
                'discount',
                'currency',
                'comment',
                'additional_info',
                'payment_terms',
                'payment_type',
                'buyers_order_number',
            ],
        )
        build_custom_form_items(
            model=Quote,
            creation_descriptor=custom_forms.QUOTE_CREATION_CFORM,
            edition_descriptor=custom_forms.QUOTE_EDITION_CFORM,
            field_names=[
                'user',
                'name',
                'number',
                'status',
                'issuing_date',
                'expiration_date',
                'discount',
                'currency',
                'comment',
                'additional_info',
                'payment_terms',
                'acceptation_date',
            ],
        )
        build_custom_form_items(
            model=SalesOrder,
            creation_descriptor=custom_forms.ORDER_CREATION_CFORM,
            edition_descriptor=custom_forms.ORDER_EDITION_CFORM,
            field_names=[
                'user',
                'name',
                'number',
                'status',
                'issuing_date',
                'expiration_date',
                'discount',
                'currency',
                'comment',
                'additional_info',
                'payment_terms',
            ],
        )
        build_custom_form_items(
            model=CreditNote,
            creation_descriptor=custom_forms.CNOTE_CREATION_CFORM,
            edition_descriptor=custom_forms.CNOTE_EDITION_CFORM,
            field_names=[
                'user',
                'name',
                'number',
                'status',
                'issuing_date',
                'expiration_date',
                'discount',
                'currency',
                'comment',
                'additional_info',
                'payment_terms',
            ],
        )

        common_template_groups_desc = [
            {
                'name':
                _('General information'),
                'cells': [
                    (EntityCellRegularField, {
                        'name': 'user'
                    }),
                    (EntityCellRegularField, {
                        'name': 'name'
                    }),
                    (EntityCellRegularField, {
                        'name': 'number'
                    }),
                    BillingTemplateStatusSubCell(
                        model=TemplateBase).into_cell(),
                    (EntityCellRegularField, {
                        'name': 'issuing_date'
                    }),
                    (EntityCellRegularField, {
                        'name': 'expiration_date'
                    }),
                    (EntityCellRegularField, {
                        'name': 'discount'
                    }),
                    (EntityCellRegularField, {
                        'name': 'currency'
                    }),
                    (EntityCellRegularField, {
                        'name': 'comment'
                    }),
                    (EntityCellRegularField, {
                        'name': 'additional_info'
                    }),
                    (EntityCellRegularField, {
                        'name': 'payment_terms'
                    }),
                    (
                        EntityCellCustomFormSpecial,
                        {
                            'name':
                            EntityCellCustomFormSpecial.REMAINING_REGULARFIELDS
                        },
                    ),
                ],
            },
            {
                'name':
                _('Organisations'),
                'cells': [
                    BillingSourceSubCell(model=TemplateBase).into_cell(),
                    BillingTargetSubCell(model=TemplateBase).into_cell(),
                ],
            },
            {
                'name': _('Description'),
                'cells': [
                    (EntityCellRegularField, {
                        'name': 'description'
                    }),
                ],
            },
            {
                'name':
                _('Custom fields'),
                'cells': [
                    (
                        EntityCellCustomFormSpecial,
                        {
                            'name':
                            EntityCellCustomFormSpecial.REMAINING_CUSTOMFIELDS
                        },
                    ),
                ],
            },
        ]
        CustomFormConfigItem.objects.create_if_needed(
            descriptor=custom_forms.BTEMPLATE_CREATION_CFORM,
            groups_desc=[
                *common_template_groups_desc,
                *creation_only_groups_desc,
            ],
        )
        CustomFormConfigItem.objects.create_if_needed(
            descriptor=custom_forms.BTEMPLATE_EDITION_CFORM,
            groups_desc=common_template_groups_desc,
        )

        # ---------------------------
        get_ct = ContentType.objects.get_for_model
        engine_id = ''
        flavour_id = ''

        if 'creme.billing.exporters.xhtml2pdf.Xhtml2pdfExportEngine' in settings.BILLING_EXPORTERS:
            from creme.billing.exporters.xhtml2pdf import Xhtml2pdfExportEngine
            from creme.creme_core.utils import l10n

            # TODO: add the country in settings & use it...
            country = l10n.FR
            language = 'fr_FR'
            theme = 'cappuccino'
            try:
                Xhtml2pdfExportEngine.FLAVOURS_INFO[country][language][theme]
            except KeyError:
                pass
            else:
                engine_id = Xhtml2pdfExportEngine.id
                flavour_id = f'{country}/{language}/{theme}'

        for model in (CreditNote, Invoice, Quote, SalesOrder, TemplateBase):
            ExporterConfigItem.objects.get_or_create(
                content_type=get_ct(model),
                defaults={
                    'engine_id': engine_id,
                    'flavour_id': flavour_id,
                },
            )

        # ---------------------------
        for model in (Invoice, CreditNote, Quote, SalesOrder):
            SearchConfigItem.objects.create_if_needed(
                model, ['name', 'number', 'status__name'])

        for model in (ProductLine, ServiceLine):
            SearchConfigItem.objects.create_if_needed(model, [], disabled=True)

        # ---------------------------
        create_svalue = SettingValue.objects.get_or_create
        create_svalue(key_id=setting_keys.payment_info_key.id,
                      defaults={'value': True})
        create_svalue(key_id=setting_keys.button_redirection_key.id,
                      defaults={'value': True})

        # ---------------------------
        # TODO: move to "not already_populated" section in creme2.4
        if not MenuConfigItem.objects.filter(
                entry_id__startswith='billing-').exists():
            container = MenuConfigItem.objects.get_or_create(
                entry_id=ContainerEntry.id,
                entry_data={'label': _('Management')},
                defaults={'order': 50},
            )[0]

            create_mitem = partial(MenuConfigItem.objects.create,
                                   parent=container)
            create_mitem(entry_id=menu.QuotesEntry.id, order=10)
            create_mitem(entry_id=menu.InvoicesEntry.id, order=15)
            create_mitem(entry_id=menu.CreditNotesEntry.id, order=50)
            create_mitem(entry_id=menu.SalesOrdersEntry.id, order=55)
            create_mitem(entry_id=menu.ProductLinesEntry.id, order=200)
            create_mitem(entry_id=menu.ServiceLinesEntry.id, order=210)

        # ---------------------------
        if not already_populated:

            def create_quote_status(pk, name, **kwargs):
                create_if_needed(QuoteStatus, {'pk': pk}, name=name, **kwargs)

            # Default status
            create_quote_status(1,
                                pgettext('billing-quote', 'Pending'),
                                order=2)

            create_quote_status(2,
                                pgettext('billing-quote', 'Accepted'),
                                order=3,
                                won=True)
            create_quote_status(3,
                                pgettext('billing-quote', 'Rejected'),
                                order=4)
            create_quote_status(4,
                                pgettext('billing-quote', 'Created'),
                                order=1)

            # ---------------------------
            create_if_needed(SettlementTerms, {'pk': 1}, name=_('30 days'))
            create_if_needed(SettlementTerms, {'pk': 2}, name=_('Cash'))
            create_if_needed(SettlementTerms, {'pk': 3}, name=_('45 days'))
            create_if_needed(SettlementTerms, {'pk': 4}, name=_('60 days'))
            create_if_needed(SettlementTerms, {'pk': 5},
                             name=_('30 days, end month the 10'))

            # ---------------------------
            create_if_needed(
                AdditionalInformation, {'pk': 1},
                name=_('Trainer accreditation'),
                description=
                _('being certified trainer courses could be supported by your OPCA'
                  ))

            # ---------------------------
            create_bmi = ButtonMenuItem.objects.create_if_needed
            create_bmi(model=Invoice,
                       button=buttons.GenerateInvoiceNumberButton,
                       order=0)

            create_bmi(model=Quote,
                       button=buttons.ConvertToInvoiceButton,
                       order=0)
            create_bmi(model=Quote,
                       button=buttons.ConvertToSalesOrderButton,
                       order=1)

            create_bmi(model=SalesOrder,
                       button=buttons.ConvertToInvoiceButton,
                       order=0)

            create_bmi(model=Organisation,
                       button=buttons.AddQuoteButton,
                       order=100)
            create_bmi(model=Organisation,
                       button=buttons.AddSalesOrderButton,
                       order=101)
            create_bmi(model=Organisation,
                       button=buttons.AddInvoiceButton,
                       order=102)

            create_bmi(model=Contact, button=buttons.AddQuoteButton, order=100)
            create_bmi(model=Contact,
                       button=buttons.AddSalesOrderButton,
                       order=101)
            create_bmi(model=Contact,
                       button=buttons.AddInvoiceButton,
                       order=102)

            # ---------------------------
            create_cbci = CustomBrickConfigItem.objects.create
            build_cell = EntityCellRegularField.build

            def build_cells(model, *extra_cells):
                return [
                    build_cell(model, 'name'),
                    build_cell(model, 'number'),
                    build_cell(model, 'issuing_date'),
                    build_cell(model, 'expiration_date'),
                    build_cell(model, 'discount'),
                    build_cell(model, 'additional_info'),
                    build_cell(model, 'payment_terms'),
                    build_cell(model, 'currency'),
                    *extra_cells,
                    build_cell(model, 'comment'),
                    build_cell(model, 'description'),
                    # --
                    build_cell(model, 'created'),
                    build_cell(model, 'modified'),
                    build_cell(model, 'user'),
                ]

            cbci_invoice = create_cbci(
                id='billing-invoice_info',
                name=_('Invoice information'),
                content_type=Invoice,
                cells=build_cells(
                    Invoice,
                    build_cell(Invoice, 'status'),
                    build_cell(Invoice, 'payment_type'),
                    build_cell(Invoice, 'buyers_order_number'),
                ),
            )
            cbci_c_note = create_cbci(
                id='billing-creditnote_info',
                name=_('Credit note information'),
                content_type=CreditNote,
                cells=build_cells(
                    CreditNote,
                    build_cell(CreditNote, 'status'),
                ),
            )
            cbci_quote = create_cbci(
                id='billing-quote_info',
                name=_('Quote information'),
                content_type=Quote,
                cells=build_cells(
                    Quote,
                    build_cell(Quote, 'status'),
                    build_cell(Quote, 'acceptation_date'),
                ),
            )
            cbci_s_order = create_cbci(
                id='billing-salesorder_info',
                name=_('Salesorder information'),
                content_type=SalesOrder,
                cells=build_cells(
                    SalesOrder,
                    build_cell(SalesOrder, 'status'),
                ),
            )
            cbci_tbase = create_cbci(
                id='billing-templatebase_info',
                name=pgettext('billing', 'Template information'),
                content_type=TemplateBase,
                cells=build_cells(
                    TemplateBase,
                    EntityCellFunctionField.build(TemplateBase,
                                                  'get_verbose_status'),
                ),
            )

            models_4_blocks = [
                (Invoice, cbci_invoice,
                 True),  # Boolean -> insert CreditNote block
                (CreditNote, cbci_c_note, False),
                (Quote, cbci_quote, True),
                (SalesOrder, cbci_s_order, True),
                (TemplateBase, cbci_tbase, False),
            ]

            TOP = BrickDetailviewLocation.TOP
            LEFT = BrickDetailviewLocation.LEFT
            RIGHT = BrickDetailviewLocation.RIGHT

            for model, cbci, has_credit_notes in models_4_blocks:
                data = [
                    # LEFT
                    {
                        'brick': cbci.brick_id,
                        'order': 5
                    },
                    {
                        'brick': core_bricks.CustomFieldsBrick,
                        'order': 40
                    },
                    {
                        'brick': bricks.BillingPaymentInformationBrick,
                        'order': 60
                    },
                    {
                        'brick': bricks.BillingPrettyAddressBrick,
                        'order': 70
                    },
                    {
                        'brick': core_bricks.PropertiesBrick,
                        'order': 450
                    },
                    {
                        'brick': core_bricks.RelationsBrick,
                        'order': 500
                    },
                    {
                        'brick': bricks.TargetBrick,
                        'order': 2,
                        'zone': RIGHT
                    },
                    {
                        'brick': bricks.TotalBrick,
                        'order': 3,
                        'zone': RIGHT
                    },
                    {
                        'brick': core_bricks.HistoryBrick,
                        'order': 20,
                        'zone': RIGHT
                    },
                    {
                        'brick': bricks.ProductLinesBrick,
                        'order': 10,
                        'zone': TOP
                    },
                    {
                        'brick': bricks.ServiceLinesBrick,
                        'order': 20,
                        'zone': TOP
                    },
                ]
                if has_credit_notes:
                    data.append({
                        'brick': bricks.CreditNotesBrick,
                        'order': 30,
                        'zone': TOP
                    })

                BrickDetailviewLocation.objects.multi_create(
                    defaults={
                        'model': model,
                        'zone': LEFT
                    },
                    data=data,
                )

            if apps.is_installed('creme.assistants'):
                logger.info(
                    'Assistants app is installed => we use the assistants blocks on detail views'
                )

                from creme.assistants import bricks as a_bricks

                for t in models_4_blocks:
                    BrickDetailviewLocation.objects.multi_create(
                        defaults={
                            'model': t[0],
                            'zone': RIGHT
                        },
                        data=[
                            {
                                'brick': a_bricks.TodosBrick,
                                'order': 100
                            },
                            {
                                'brick': a_bricks.MemosBrick,
                                'order': 200
                            },
                            {
                                'brick': a_bricks.AlertsBrick,
                                'order': 300
                            },
                            {
                                'brick': a_bricks.UserMessagesBrick,
                                'order': 400
                            },
                        ],
                    )

            if apps.is_installed('creme.documents'):
                # logger.info(
                #   'Documents app is installed
                #   => we use the documents block on detail views'
                # )

                from creme.documents.bricks import LinkedDocsBrick

                for t in models_4_blocks:
                    BrickDetailviewLocation.objects.create_if_needed(
                        brick=LinkedDocsBrick,
                        order=600,
                        zone=RIGHT,
                        model=t[0],
                    )

            BrickDetailviewLocation.objects.multi_create(
                defaults={
                    'model': Organisation,
                    'zone': RIGHT
                },
                data=[
                    {
                        'brick': bricks.PaymentInformationBrick,
                        'order': 300,
                        'zone': LEFT
                    },
                    {
                        'brick': bricks.ReceivedInvoicesBrick,
                        'order': 14
                    },
                    {
                        'brick': bricks.ReceivedQuotesBrick,
                        'order': 18
                    },
                ],
            )

            # ---------------------------
            if apps.is_installed('creme.reports'):
                logger.info(
                    'Reports app is installed '
                    '=> we create 2 billing reports, with 3 graphs, and related blocks in home'
                )
                self.create_reports(
                    rt_sub_bill_received,
                    current_year_invoice_filter,
                    current_year_unpaid_invoice_filter,
                )
示例#7
0
# -*- coding: utf-8 -*-

from django.utils.translation import gettext_lazy as _

from creme import products
from creme.creme_core.gui.custom_form import CustomFormDescriptor
from creme.products.forms.base import SubCategorySubCell

Product = products.get_product_model()
Service = products.get_service_model()

PRODUCT_CREATION_CFORM = CustomFormDescriptor(
    id='products-product_creation',
    model=Product,
    verbose_name=_('Creation form for product'),
    excluded_fields=('category', 'sub_category'),
    extra_sub_cells=[SubCategorySubCell(model=Product)],
)
PRODUCT_EDITION_CFORM = CustomFormDescriptor(
    id='products-product_edition',
    model=Product,
    form_type=CustomFormDescriptor.EDITION_FORM,
    verbose_name=_('Edition form for product'),
    excluded_fields=('category', 'sub_category', 'images'),
    extra_sub_cells=[SubCategorySubCell(model=Product)],
)
SERVICE_CREATION_CFORM = CustomFormDescriptor(
    id='products-service_creation',
    model=Service,
    verbose_name=_('Creation form for service'),
    excluded_fields=('category', 'sub_category'),
示例#8
0
    CreditNote = billing.get_credit_note_model()
    Invoice = billing.get_invoice_model()
    Quote = billing.get_quote_model()
    SalesOrder = billing.get_sales_order_model()
    TemplateBase = billing.get_template_base_model()

    ProductLine = billing.get_product_line_model()
    ServiceLine = billing.get_service_line_model()
except Exception as e:
    print('Error in <{}>: {}'.format(__name__, e))

Address = get_address_model()
Contact = get_contact_model()
Organisation = get_organisation_model()

Product = get_product_model()
Service = get_service_model()


def skipIfCustomCreditNote(test_func):
    return skipIf(skip_cnote_tests,
                  'Custom CreditNote model in use')(test_func)


def skipIfCustomInvoice(test_func):
    return skipIf(skip_invoice_tests, 'Custom Invoice model in use')(test_func)


def skipIfCustomQuote(test_func):
    return skipIf(skip_quote_tests, 'Custom Quote model in use')(test_func)
示例#9
0
    def test_populate(self):  # test get_compatible_ones() too
        ct = ContentType.objects.get_for_model(Opportunity)
        relation_types = RelationType.objects.compatible(ct).in_bulk()

        Product = products.get_product_model()
        Service = products.get_service_model()

        self.assertNotIn(constants.REL_SUB_TARGETS, relation_types)
        self.get_relationtype_or_fail(
            constants.REL_SUB_TARGETS,
            [Opportunity],
            [Contact, Organisation],
        )

        self.assertNotIn(constants.REL_SUB_EMIT_ORGA, relation_types)
        self.get_relationtype_or_fail(
            constants.REL_OBJ_EMIT_ORGA,
            [Opportunity],
            [Organisation],
        )

        self.assertIn(constants.REL_OBJ_LINKED_PRODUCT, relation_types)
        self.assertNotIn(constants.REL_SUB_LINKED_PRODUCT, relation_types)
        self.get_relationtype_or_fail(
            constants.REL_OBJ_LINKED_PRODUCT,
            [Opportunity],
            [Product],
        )

        self.assertIn(constants.REL_OBJ_LINKED_SERVICE, relation_types)
        self.assertNotIn(constants.REL_SUB_LINKED_SERVICE, relation_types)
        self.get_relationtype_or_fail(
            constants.REL_OBJ_LINKED_SERVICE,
            [Opportunity],
            [Service],
        )

        self.assertIn(constants.REL_OBJ_LINKED_CONTACT, relation_types)
        self.assertNotIn(constants.REL_SUB_LINKED_CONTACT, relation_types)
        self.get_relationtype_or_fail(
            constants.REL_OBJ_LINKED_CONTACT,
            [Opportunity],
            [Contact],
        )

        self.assertIn(constants.REL_OBJ_RESPONSIBLE, relation_types)
        self.assertNotIn(constants.REL_SUB_RESPONSIBLE, relation_types)
        self.get_relationtype_or_fail(
            constants.REL_OBJ_RESPONSIBLE,
            [Opportunity],
            [Contact],
        )

        self.assertTrue(SalesPhase.objects.exists())
        self.assertTrue(Origin.objects.exists())

        def assertSVEqual(key, value):
            with self.assertNoException():
                sv = SettingValue.objects.get_4_key(key)

            self.assertIs(sv.value, value)

        assertSVEqual(setting_keys.quote_key, False)
        assertSVEqual(setting_keys.target_constraint_key, True)
        assertSVEqual(setting_keys.emitter_constraint_key, True)
示例#10
0
 def related_item_class():
     return get_product_model()
示例#11
0
    def populate(self):
        already_populated = RelationType.objects.filter(pk=constants.REL_SUB_BILL_ISSUED).exists()

        Contact      = persons.get_contact_model()
        Organisation = persons.get_organisation_model()
        Product = products.get_product_model()
        Service = products.get_service_model()

        # ---------------------------
        # line_entities = [ProductLine, ServiceLine]
        line_entities = list(lines_registry)
        RelationType.create((constants.REL_SUB_BILL_ISSUED, _('issued by'),  BILLING_MODELS),
                            (constants.REL_OBJ_BILL_ISSUED, _('has issued'), [Organisation]),
                            is_internal=True,
                            minimal_display=(False, True),
                           )
        rt_sub_bill_received = \
        RelationType.create((constants.REL_SUB_BILL_RECEIVED, _('received by'),  BILLING_MODELS),
                            (constants.REL_OBJ_BILL_RECEIVED, _('has received'), [Organisation, Contact]),
                            is_internal=True,
                            minimal_display=(False, True),
                           )[0]
        RelationType.create((constants.REL_SUB_HAS_LINE, _('had the line'),   BILLING_MODELS),
                            (constants.REL_OBJ_HAS_LINE, _('is the line of'), line_entities),
                            is_internal=True,
                            minimal_display=(True, True),
                           )
        RelationType.create((constants.REL_SUB_LINE_RELATED_ITEM, _('has the related item'),   line_entities),
                            (constants.REL_OBJ_LINE_RELATED_ITEM, _('is the related item of'), [Product, Service]),
                            is_internal=True,
                           )
        RelationType.create((constants.REL_SUB_CREDIT_NOTE_APPLIED, _('is used in the billing document'), [CreditNote]),
                            (constants.REL_OBJ_CREDIT_NOTE_APPLIED, _('used the credit note'),            [Quote, SalesOrder, Invoice]),
                            is_internal=True,
                            minimal_display=(True, True),
                           )

        if apps.is_installed('creme.activities'):
            logger.info('Activities app is installed => an Invoice/Quote/SalesOrder can be the subject of an Activity')

            from creme.activities.constants import REL_SUB_ACTIVITY_SUBJECT

            RelationType.objects.get(pk=REL_SUB_ACTIVITY_SUBJECT) \
                                .add_subject_ctypes(Invoice, Quote, SalesOrder)

        # ---------------------------
        create_if_needed(PaymentTerms, {'pk': 1}, name=_('Deposit'),
                         description=_(r'20% deposit will be required'),
                         is_custom=False,
                        )

        # ---------------------------
        # NB: pk=1 + is_custom=False --> default status (used when a quote is converted in invoice for example)
        create_if_needed(SalesOrderStatus, {'pk': 1}, name=pgettext('billing-salesorder', 'Issued'), order=1, is_custom=False) # Default status
        if not already_populated:
            create_if_needed(SalesOrderStatus, {'pk': 2}, name=pgettext('billing-salesorder', 'Accepted'), order=3)
            create_if_needed(SalesOrderStatus, {'pk': 3}, name=pgettext('billing-salesorder', 'Rejected'), order=4)
            create_if_needed(SalesOrderStatus, {'pk': 4}, name=pgettext('billing-salesorder', 'Created'),  order=2)

        # ---------------------------
        def create_invoice_status(pk, name, **kwargs):
            create_if_needed(InvoiceStatus, {'pk': pk}, name=name, **kwargs)

        create_invoice_status(1, pgettext('billing-invoice', 'Draft'),      order=1, is_custom=False)  # Default status
        create_invoice_status(2, pgettext('billing-invoice', 'To be sent'), order=2, is_custom=False)
        if not already_populated:
            create_invoice_status(3, pgettext('billing-invoice', 'Sent'),            order=3, pending_payment=True)
            create_invoice_status(4, pgettext('billing-invoice', 'Resulted'),        order=5)
            create_invoice_status(5, pgettext('billing-invoice', 'Partly resulted'), order=4, pending_payment=True)
            create_invoice_status(6, _('Collection'),                                order=7)
            create_invoice_status(7, _('Resulted collection'),                       order=6)
            create_invoice_status(8, pgettext('billing-invoice', 'Canceled'),        order=8)

        # ---------------------------
        create_if_needed(CreditNoteStatus, {'pk': 1}, name=pgettext('billing-creditnote', 'Draft'), order=1, is_custom=False)
        if not already_populated:
            create_if_needed(CreditNoteStatus, {'pk': 2}, name=pgettext('billing-creditnote', 'Issued'),      order=2)
            create_if_needed(CreditNoteStatus, {'pk': 3}, name=pgettext('billing-creditnote', 'Consumed'),    order=3)
            create_if_needed(CreditNoteStatus, {'pk': 4}, name=pgettext('billing-creditnote', 'Out of date'), order=4)

        # ---------------------------
        EntityFilter.create(
                'billing-invoices_unpaid', name=_('Invoices unpaid'),
                model=Invoice, user='******',
                conditions=[EntityFilterCondition.build_4_field(
                                    model=Invoice,
                                    operator=EntityFilterCondition.EQUALS,
                                    name='status__pending_payment', values=[True],
                                ),
                           ],
            )
        EntityFilter.create(
                'billing-invoices_unpaid_late', name=_('Invoices unpaid and late'),
                model=Invoice, user='******',
                conditions=[EntityFilterCondition.build_4_field(
                                    model=Invoice,
                                    operator=EntityFilterCondition.EQUALS,
                                    name='status__pending_payment', values=[True],
                                ),
                            EntityFilterCondition.build_4_date(
                                    model=Invoice,
                                    name='expiration_date', date_range='in_past',
                                ),
                           ],
            )
        current_year_invoice_filter = EntityFilter.create(
                'billing-current_year_invoices', name=_('Current year invoices'),
                model=Invoice, user='******',
                conditions=[EntityFilterCondition.build_4_date(
                                    model=Invoice,
                                    name='issuing_date', date_range='current_year',
                                ),
                           ],
            )
        current_year_unpaid_invoice_filter = EntityFilter.create(
                'billing-current_year_unpaid_invoices',
                name=_('Current year and unpaid invoices'),
                model=Invoice, user='******',
                conditions=[EntityFilterCondition.build_4_date(
                                    model=Invoice,
                                    name='issuing_date', date_range='current_year',
                                ),
                            EntityFilterCondition.build_4_field(
                                    model=Invoice,
                                    operator=EntityFilterCondition.EQUALS,
                                    name='status__pending_payment', values=[True],
                                ),
                           ],
            )

        # ---------------------------
        def create_hf(hf_pk, name, model, status=True):
            HeaderFilter.create(pk=hf_pk, name=name, model=model,
                                cells_desc=[(EntityCellRegularField, {'name': 'name'}),
                                            EntityCellRelation(model=model, rtype=rt_sub_bill_received),
                                            (EntityCellRegularField, {'name': 'number'}),
                                            (EntityCellRegularField, {'name': 'status'}) if status else None,
                                            (EntityCellRegularField, {'name': 'total_no_vat'}),
                                            (EntityCellRegularField, {'name': 'issuing_date'}),
                                            (EntityCellRegularField, {'name': 'expiration_date'}),
                                           ],
                               )

        create_hf(constants.DEFAULT_HFILTER_INVOICE,  _('Invoice view'),     Invoice)
        create_hf(constants.DEFAULT_HFILTER_QUOTE,    _('Quote view'),       Quote)
        create_hf(constants.DEFAULT_HFILTER_ORDER,    _('Sales order view'), SalesOrder)
        create_hf(constants.DEFAULT_HFILTER_CNOTE,    _('Credit note view'), CreditNote)
        create_hf(constants.DEFAULT_HFILTER_TEMPLATE, _('Template view'),    TemplateBase, status=False)

        def create_hf_lines(hf_pk, name, model):
            build_cell = EntityCellRegularField.build
            HeaderFilter.create(pk=hf_pk, name=name, model=model,
                                cells_desc=[build_cell(model=model, name='on_the_fly_item'),
                                            build_cell(model=model, name='quantity'),
                                            build_cell(model=model, name='unit_price'),
                                           ]
                               )

        create_hf_lines('billing-hg_product_lines', _('Product lines view'), ProductLine)
        create_hf_lines('billing-hg_service_lines', _('Service lines view'), ServiceLine)

        # ---------------------------
        for model in (Invoice, CreditNote, Quote, SalesOrder):
            SearchConfigItem.create_if_needed(model, ['name', 'number', 'status__name'])

        for model in (ProductLine, ServiceLine):
            SearchConfigItem.create_if_needed(model, [], disabled=True)

        # ---------------------------
        SettingValue.objects.get_or_create(key_id=setting_keys.payment_info_key.id, defaults={'value': True})

        # ---------------------------
        if not already_populated:
            create_if_needed(QuoteStatus, {'pk': 1}, name=pgettext('billing-quote', 'Pending'),  order=2)  # Default status
            create_if_needed(QuoteStatus, {'pk': 2}, name=pgettext('billing-quote', 'Accepted'), order=3, won=True)
            create_if_needed(QuoteStatus, {'pk': 3}, name=pgettext('billing-quote', 'Rejected'), order=4)
            create_if_needed(QuoteStatus, {'pk': 4}, name=pgettext('billing-quote', 'Created'),  order=1)

            # ---------------------------
            create_if_needed(SettlementTerms, {'pk': 1}, name=_('30 days'))
            create_if_needed(SettlementTerms, {'pk': 2}, name=_('Cash'))
            create_if_needed(SettlementTerms, {'pk': 3}, name=_('45 days'))
            create_if_needed(SettlementTerms, {'pk': 4}, name=_('60 days'))
            create_if_needed(SettlementTerms, {'pk': 5}, name=_('30 days, end month the 10'))

            # ---------------------------
            create_if_needed(AdditionalInformation, {'pk': 1}, name=_('Trainer accreditation'),
                             description=_('being certified trainer courses could be supported by your OPCA')
                            )

            # ---------------------------
            create_bmi = ButtonMenuItem.create_if_needed
            create_bmi(pk='billing-generate_invoice_number', model=Invoice, button=buttons.GenerateInvoiceNumberButton, order=0)

            create_bmi(pk='billing-quote_orga_button',      model=Organisation, button=buttons.AddQuoteButton,      order=100)
            create_bmi(pk='billing-salesorder_orga_button', model=Organisation, button=buttons.AddSalesOrderButton, order=101)
            create_bmi(pk='billing-invoice_orga_button',    model=Organisation, button=buttons.AddInvoiceButton,    order=102)

            create_bmi(pk='billing-quote_contact_button',      model=Contact, button=buttons.AddQuoteButton,      order=100)
            create_bmi(pk='billing-salesorder_contact_button', model=Contact, button=buttons.AddSalesOrderButton, order=101)
            create_bmi(pk='billing-invoice_contact_button',    model=Contact, button=buttons.AddInvoiceButton,    order=102)

            # ---------------------------
            get_ct = ContentType.objects.get_for_model
            create_cbci = CustomBrickConfigItem.objects.create
            build_cell = EntityCellRegularField.build

            def build_cells(model, *extra_cells):
                return [
                    build_cell(model, 'name'),
                    build_cell(model, 'number'),
                    build_cell(model, 'issuing_date'),
                    build_cell(model, 'expiration_date'),
                    build_cell(model, 'discount'),
                    build_cell(model, 'additional_info'),
                    build_cell(model, 'payment_terms'),
                    build_cell(model, 'currency'),
                ] + list(extra_cells) + [
                    build_cell(model, 'comment'),
                    # --
                    build_cell(model, 'created'),
                    build_cell(model, 'modified'),
                    build_cell(model, 'user'),
                ]

            cbci_invoice = create_cbci(id='billing-invoice_info',
                                       name=_('Invoice information'),
                                       content_type=get_ct(Invoice),
                                       cells=build_cells(Invoice,
                                                         build_cell(Invoice, 'status'),
                                                         build_cell(Invoice, 'payment_type'),
                                                        ),
                                      )
            cbci_c_note   = create_cbci(id='billing-creditnote_info',
                                        name=_('Credit note information'),
                                        content_type=get_ct(CreditNote),
                                        cells=build_cells(CreditNote, build_cell(CreditNote, 'status')),
                                       )
            cbci_quote   = create_cbci(id='billing-quote_info',
                                       name=_('Quote information'),
                                       content_type=get_ct(Quote),
                                       cells=build_cells(Quote,
                                                         build_cell(Quote, 'status'),
                                                         build_cell(Quote, 'acceptation_date'),
                                                        ),
                                      )
            cbci_s_order = create_cbci(id='billing-salesorder_info',
                                       name=_('Salesorder information'),
                                       content_type=get_ct(SalesOrder),
                                       cells=build_cells(SalesOrder, build_cell(SalesOrder, 'status')),
                                      )
            cbci_tbase   = create_cbci(id='billing-templatebase_info',
                                       name=pgettext('billing', 'Template information'),
                                       content_type=get_ct(TemplateBase),
                                       cells=build_cells(TemplateBase,
                                                         EntityCellFunctionField.build(TemplateBase, 'get_verbose_status'),
                                                        ),
                                      )

            models_4_blocks = [(Invoice,      cbci_invoice, True),  # Boolean -> insert CreditNote block
                               (CreditNote,   cbci_c_note,  False),
                               (Quote,        cbci_quote,   True),
                               (SalesOrder,   cbci_s_order, True),
                               (TemplateBase, cbci_tbase,   False),
                              ]
            create_bdl = BrickDetailviewLocation.create_if_needed
            TOP   = BrickDetailviewLocation.TOP
            LEFT  = BrickDetailviewLocation.LEFT
            RIGHT = BrickDetailviewLocation.RIGHT

            for model, cbci, has_credit_notes in models_4_blocks:
                create_bdl(brick_id=bricks.ProductLinesBrick.id_, order=10, zone=TOP, model=model)
                create_bdl(brick_id=bricks.ServiceLinesBrick.id_, order=20, zone=TOP, model=model)

                if has_credit_notes:
                    create_bdl(brick_id=bricks.CreditNotesBrick.id_, order=30, zone=TOP, model=model)

                create_bdl(brick_id=cbci.generate_id(),                        order=5,   zone=LEFT, model=model)
                create_bdl(brick_id=core_bricks.CustomFieldsBrick.id_,         order=40,  zone=LEFT, model=model)
                create_bdl(brick_id=bricks.BillingPaymentInformationBrick.id_, order=60,  zone=LEFT, model=model)
                create_bdl(brick_id=bricks.BillingPrettyAddressBrick.id_,      order=70,  zone=LEFT, model=model)
                create_bdl(brick_id=core_bricks.PropertiesBrick.id_,           order=450, zone=LEFT, model=model)
                create_bdl(brick_id=core_bricks.RelationsBrick.id_,            order=500, zone=LEFT, model=model)

                create_bdl(brick_id=bricks.TargetBrick.id_,       order=2,  zone=RIGHT, model=model)
                create_bdl(brick_id=bricks.TotalBrick.id_,        order=3,  zone=RIGHT, model=model)
                create_bdl(brick_id=core_bricks.HistoryBrick.id_, order=20, zone=RIGHT, model=model)

            if apps.is_installed('creme.assistants'):
                logger.info('Assistants app is installed => we use the assistants blocks on detail views')

                from creme.assistants.bricks import AlertsBrick, MemosBrick, TodosBrick, UserMessagesBrick

                for t in models_4_blocks:
                    model = t[0]
                    create_bdl(brick_id=TodosBrick.id_,        order=100, zone=RIGHT, model=model)
                    create_bdl(brick_id=MemosBrick.id_,        order=200, zone=RIGHT, model=model)
                    create_bdl(brick_id=AlertsBrick.id_,       order=300, zone=RIGHT, model=model)
                    create_bdl(brick_id=UserMessagesBrick.id_, order=400, zone=RIGHT, model=model)

            if apps.is_installed('creme.documents'):
                # logger.info('Documents app is installed => we use the documents block on detail views')

                from creme.documents.bricks import LinkedDocsBrick

                for t in models_4_blocks:
                    create_bdl(brick_id=LinkedDocsBrick.id_, order=600, zone=RIGHT, model=t[0])

            create_bdl(brick_id=bricks.PaymentInformationBrick.id_, order=300, zone=LEFT,  model=Organisation)
            create_bdl(brick_id=bricks.ReceivedInvoicesBrick.id_,   order=14,  zone=RIGHT, model=Organisation)
            create_bdl(brick_id=bricks.ReceivedQuotesBrick.id_,     order=18,  zone=RIGHT, model=Organisation)

            # ---------------------------
            if apps.is_installed('creme.reports'):
                logger.info('Reports app is installed => we create 2 billing reports, with 3 graphs, and related blocks in home')
                self.create_reports(rt_sub_bill_received,
                                    current_year_invoice_filter,
                                    current_year_unpaid_invoice_filter,
                                   )
示例#12
0
    def populate(self):
        already_populated = RelationType.objects.filter(
            pk=constants.REL_SUB_SOLD).exists()

        Act = commercial.get_act_model()
        ActObjectivePattern = commercial.get_pattern_model()
        Strategy = commercial.get_strategy_model()
        Contact = persons.get_contact_model()
        Organisation = persons.get_organisation_model()
        Product = products.get_product_model()
        Service = products.get_service_model()

        RelationType.create(
            (constants.REL_SUB_SOLD, _('has sold'), [Contact, Organisation]),
            (constants.REL_OBJ_SOLD, _('has been sold by'), [Product, Service
                                                             ]),
        )

        complete_goal_models = {*creme_registry.iter_entity_models()}
        complete_goal_models.discard(Strategy)
        if apps.is_installed('creme.billing'):
            from creme import billing
            from creme.billing.registry import lines_registry

            complete_goal_models.discard(billing.get_credit_note_model())
            complete_goal_models.discard(billing.get_template_base_model())
            complete_goal_models.difference_update(lines_registry)

        RelationType.create(
            (
                constants.REL_SUB_COMPLETE_GOAL,
                _('completes a goal of the commercial action'),
                complete_goal_models,
            ),
            (
                constants.REL_OBJ_COMPLETE_GOAL,
                _('is completed thanks to'),
                [Act],
            ),
        )

        # ---------------------------
        CremePropertyType.create(constants.PROP_IS_A_SALESMAN,
                                 _('is a salesman'), [Contact])

        # ---------------------------
        MarketSegment.objects.get_or_create(
            property_type=None,
            defaults={'name': _('All the organisations')},
        )

        # ---------------------------
        for i, title in enumerate(
            [_('Phone calls'), _('Show'),
             _('Demo')], start=1):
            create_if_needed(ActType, {'pk': i}, title=title, is_custom=False)

        # ---------------------------
        create_hf = HeaderFilter.objects.create_if_needed
        create_hf(
            pk=constants.DEFAULT_HFILTER_ACT,
            model=Act,
            name=_('Com Action view'),
            cells_desc=[
                (EntityCellRegularField, {
                    'name': 'name'
                }),
                (EntityCellRegularField, {
                    'name': 'expected_sales'
                }),
                (EntityCellRegularField, {
                    'name': 'due_date'
                }),
            ],
        )
        create_hf(
            pk=constants.DEFAULT_HFILTER_STRATEGY,
            model=Strategy,
            name=_('Strategy view'),
            cells_desc=[(EntityCellRegularField, {
                'name': 'name'
            })],
        )
        create_hf(
            pk=constants.DEFAULT_HFILTER_PATTERN,
            model=ActObjectivePattern,
            name=_('Objective pattern view'),
            cells_desc=[
                (EntityCellRegularField, {
                    'name': 'name'
                }),
                (EntityCellRegularField, {
                    'name': 'segment'
                }),
            ],
        )

        # ---------------------------
        def build_custom_form_items(creation_descriptor, edition_descriptor,
                                    field_names):
            base_groups_desc = [
                {
                    'name':
                    _('General information'),
                    'layout':
                    LAYOUT_DUAL_FIRST,
                    'cells': [
                        *((EntityCellRegularField, {
                            'name': fname
                        }) for fname in field_names),
                        (
                            EntityCellCustomFormSpecial,
                            {
                                'name':
                                EntityCellCustomFormSpecial.
                                REMAINING_REGULARFIELDS
                            },
                        ),
                    ],
                },
                {
                    'name': _('Description'),
                    'layout': LAYOUT_DUAL_SECOND,
                    'cells': [
                        (EntityCellRegularField, {
                            'name': 'description'
                        }),
                    ],
                },
                {
                    'name':
                    _('Custom fields'),
                    'layout':
                    LAYOUT_DUAL_SECOND,
                    'cells': [
                        (
                            EntityCellCustomFormSpecial,
                            {
                                'name':
                                EntityCellCustomFormSpecial.
                                REMAINING_CUSTOMFIELDS
                            },
                        ),
                    ],
                },
            ]

            CustomFormConfigItem.objects.create_if_needed(
                descriptor=creation_descriptor,
                groups_desc=[
                    *base_groups_desc,
                    {
                        'name':
                        _('Properties'),
                        'cells': [
                            (
                                EntityCellCustomFormSpecial,
                                {
                                    'name':
                                    EntityCellCustomFormSpecial.
                                    CREME_PROPERTIES
                                },
                            ),
                        ],
                    },
                    {
                        'name':
                        _('Relationships'),
                        'cells': [
                            (
                                EntityCellCustomFormSpecial,
                                {
                                    'name':
                                    EntityCellCustomFormSpecial.RELATIONS
                                },
                            ),
                        ],
                    },
                ],
            )
            CustomFormConfigItem.objects.create_if_needed(
                descriptor=edition_descriptor,
                groups_desc=base_groups_desc,
            )

        build_custom_form_items(
            creation_descriptor=custom_forms.ACT_CREATION_CFORM,
            edition_descriptor=custom_forms.ACT_EDITION_CFORM,
            field_names=[
                'user',
                'name',
                'expected_sales',
                'cost',
                'goal',
                'start',
                'due_date',
                'act_type',
                'segment',
            ],
        )
        build_custom_form_items(
            creation_descriptor=custom_forms.PATTERN_CREATION_CFORM,
            edition_descriptor=custom_forms.PATTERN_EDITION_CFORM,
            field_names=[
                'user',
                'name',
                'average_sales',
                'segment',
            ],
        )
        build_custom_form_items(
            creation_descriptor=custom_forms.STRATEGY_CREATION_CFORM,
            edition_descriptor=custom_forms.STRATEGY_EDITION_CFORM,
            field_names=[
                'user',
                'name',
            ],
        )

        # ---------------------------
        create_searchconf = SearchConfigItem.objects.create_if_needed
        create_searchconf(Act, ['name', 'expected_sales', 'cost', 'goal'])
        create_searchconf(Strategy, ['name'])
        create_searchconf(ActObjectivePattern, [], disabled=True)

        # ---------------------------
        SettingValue.objects.get_or_create(
            key_id=setting_keys.orga_approaches_key.id,
            defaults={'value': True},
        )

        # ---------------------------
        Job.objects.get_or_create(
            type_id=creme_jobs.com_approaches_emails_send_type.id,
            defaults={
                'language': settings.LANGUAGE_CODE,
                'periodicity': date_period_registry.get_period('days', 1),
                'status': Job.STATUS_OK,
                # The CommercialApproach field for Activities' CustomForms is not
                # in the default configuration, so a enabled job would be annoying.
                'enabled': False,
            },
        )

        # ---------------------------
        # TODO: move to "not already_populated" section in creme2.4
        if not MenuConfigItem.objects.filter(
                entry_id__startswith='commercial-').exists():
            container = MenuConfigItem.objects.get_or_create(
                entry_id=ContainerEntry.id,
                entry_data={'label': _('Commercial')},
                defaults={'order': 30},
            )[0]

            create_mitem = MenuConfigItem.objects.create
            create_mitem(entry_id=menu.ActsEntry.id,
                         order=50,
                         parent=container)
            create_mitem(entry_id=menu.StrategiesEntry.id,
                         order=55,
                         parent=container)
            create_mitem(entry_id=menu.SegmentsEntry.id,
                         order=60,
                         parent=container)
            create_mitem(entry_id=menu.PatternsEntry.id,
                         order=70,
                         parent=container)

            directory = MenuConfigItem.objects.filter(
                entry_id=ContainerEntry.id,
                entry_data={
                    'label': _('Directory')
                },
            ).first()
            if directory is not None:
                create_mitem(entry_id=menu.SalesmenEntry.id,
                             order=100,
                             parent=directory)

            creations = MenuConfigItem.objects.filter(
                entry_id=ContainerEntry.id,
                entry_data={
                    'label': _('+ Creation')
                },
            ).first()
            if creations is not None:
                create_mitem(entry_id=menu.ActCreationEntry.id,
                             order=40,
                             parent=creations)

        # ---------------------------
        if not already_populated:
            ButtonMenuItem.objects.create_if_needed(
                button=buttons.CompleteGoalButton,
                order=60,
            )

            TOP = BrickDetailviewLocation.TOP
            RIGHT = BrickDetailviewLocation.RIGHT
            LEFT = BrickDetailviewLocation.LEFT

            # BrickDetailviewLocation.objects.multi_create(
            #     defaults={'brick': bricks.ApproachesBrick, 'order': 10, 'zone': RIGHT},
            #     data=[
            #         {},  # default configuration
            #         {'model': Contact},
            #         {'model': Organisation},
            #     ]
            # )

            BrickDetailviewLocation.objects.multi_create(
                defaults={
                    'model': Act,
                    'zone': LEFT
                },
                data=[
                    {
                        'order': 5
                    },  # generic information brick
                    {
                        'brick': bricks.ActObjectivesBrick,
                        'order': 10
                    },
                    {
                        'brick': bricks.RelatedOpportunitiesBrick,
                        'order': 20
                    },
                    {
                        'brick': core_bricks.CustomFieldsBrick,
                        'order': 40
                    },
                    {
                        'brick': core_bricks.PropertiesBrick,
                        'order': 450
                    },
                    {
                        'brick': core_bricks.RelationsBrick,
                        'order': 500
                    },
                    {
                        'brick': core_bricks.HistoryBrick,
                        'order': 20,
                        'zone': RIGHT
                    },
                ],
            )
            BrickDetailviewLocation.objects.multi_create(
                defaults={
                    'model': ActObjectivePattern,
                    'zone': LEFT
                },
                data=[
                    {
                        'brick': bricks.PatternComponentsBrick,
                        'order': 10,
                        'zone': TOP
                    },
                    {
                        'order': 5
                    },
                    {
                        'brick': core_bricks.CustomFieldsBrick,
                        'order': 40
                    },
                    {
                        'brick': core_bricks.PropertiesBrick,
                        'order': 450
                    },
                    {
                        'brick': core_bricks.RelationsBrick,
                        'order': 500
                    },
                    {
                        'brick': core_bricks.HistoryBrick,
                        'order': 20,
                        'zone': RIGHT
                    },
                ],
            )
            BrickDetailviewLocation.objects.multi_create(
                defaults={
                    'model': Strategy,
                    'zone': LEFT
                },
                data=[
                    {
                        'brick': bricks.SegmentDescriptionsBrick,
                        'order': 10,
                        'zone': TOP
                    },
                    {
                        'order': 5
                    },
                    {
                        'brick': core_bricks.CustomFieldsBrick,
                        'order': 40
                    },
                    {
                        'brick': bricks.EvaluatedOrgasBrick,
                        'order': 50
                    },
                    {
                        'brick': bricks.AssetsBrick,
                        'order': 60
                    },
                    {
                        'brick': bricks.CharmsBrick,
                        'order': 70
                    },
                    {
                        'brick': core_bricks.PropertiesBrick,
                        'order': 450
                    },
                    {
                        'brick': core_bricks.RelationsBrick,
                        'order': 500
                    },
                    {
                        'brick': core_bricks.HistoryBrick,
                        'order': 20,
                        'zone': RIGHT
                    },
                ],
            )

            if apps.is_installed('creme.assistants'):
                logger.info('Assistants app is installed '
                            '=> we use the assistants blocks on detail views')

                from creme.assistants import bricks as a_bricks

                for model in (Act, ActObjectivePattern, Strategy):
                    BrickDetailviewLocation.objects.multi_create(
                        defaults={
                            'model': model,
                            'zone': RIGHT
                        },
                        data=[
                            {
                                'brick': a_bricks.TodosBrick,
                                'order': 100
                            },
                            {
                                'brick': a_bricks.MemosBrick,
                                'order': 200
                            },
                            {
                                'brick': a_bricks.AlertsBrick,
                                'order': 300
                            },
                            {
                                'brick': a_bricks.UserMessagesBrick,
                                'order': 400
                            },
                        ],
                    )

            if apps.is_installed('creme.documents'):
                # logger.info("Documents app is installed
                # => we use the documents blocks on Strategy's detail views")

                from creme.documents.bricks import LinkedDocsBrick

                BrickDetailviewLocation.objects.multi_create(
                    defaults={
                        'brick': LinkedDocsBrick,
                        'order': 600,
                        'zone': RIGHT
                    },
                    data=[
                        {
                            'model': Act
                        },
                        {
                            'model': ActObjectivePattern
                        },
                        {
                            'model': Strategy
                        },
                    ],
                )
示例#13
0
    def populate(self):
        already_populated = RelationType.objects.filter(
            pk=constants.REL_SUB_TARGETS).exists()

        Contact = persons.get_contact_model()
        Organisation = persons.get_organisation_model()
        Product = products.get_product_model()
        Service = products.get_service_model()

        # ---------------------------
        create_rtype = RelationType.create
        rt_sub_targets = create_rtype(
            (
                constants.REL_SUB_TARGETS,
                _('targets the organisation/contact'),
                [Opportunity],
            ),
            (
                constants.REL_OBJ_TARGETS,
                _('targeted by the opportunity'),
                [Organisation, Contact],
            ),
            is_internal=True,
            minimal_display=(True, True),
        )[0]
        rt_obj_emit_orga = create_rtype(
            (
                constants.REL_SUB_EMIT_ORGA,
                _('has generated the opportunity'),
                [Organisation],
            ),
            (
                constants.REL_OBJ_EMIT_ORGA,
                _('has been generated by'),
                [Opportunity],
            ),
            is_internal=True,
            minimal_display=(True, True),
        )[1]
        create_rtype((constants.REL_SUB_LINKED_PRODUCT,
                      _('is linked to the opportunity'), [Product]),
                     (constants.REL_OBJ_LINKED_PRODUCT,
                      _('concerns the product'), [Opportunity]))
        create_rtype(
            (constants.REL_SUB_LINKED_SERVICE,
             _('is linked to the opportunity'), [Service]),
            (constants.REL_OBJ_LINKED_SERVICE, _('concerns the service'),
             [Opportunity]),
        )
        create_rtype(
            (constants.REL_SUB_LINKED_CONTACT,
             _('involves in the opportunity'), [Contact]),
            (constants.REL_OBJ_LINKED_CONTACT, _('stages'), [Opportunity])),
        create_rtype(
            (constants.REL_SUB_RESPONSIBLE, _('is responsible for'), [Contact
                                                                      ]),
            (constants.REL_OBJ_RESPONSIBLE, _('has as responsible contact'),
             [Opportunity]),
        )

        if apps.is_installed('creme.activities'):
            logger.info('Activities app is installed'
                        ' => an Opportunity can be the subject of an Activity')

            from creme.activities.constants import REL_SUB_ACTIVITY_SUBJECT

            RelationType.objects.get(
                pk=REL_SUB_ACTIVITY_SUBJECT, ).add_subject_ctypes(Opportunity)

        if apps.is_installed('creme.billing'):
            logger.info(
                'Billing app is installed'
                ' => we create relationships between Opportunities & billing models'
            )

            from creme import billing

            Invoice = billing.get_invoice_model()
            Quote = billing.get_quote_model()
            SalesOrder = billing.get_sales_order_model()

            create_rtype(
                (
                    constants.REL_SUB_LINKED_SALESORDER,
                    _('is associate with the opportunity'),
                    [SalesOrder],
                ),
                (constants.REL_OBJ_LINKED_SALESORDER,
                 _('has generated the salesorder'), [Opportunity]),
            )
            create_rtype(
                (
                    constants.REL_SUB_LINKED_INVOICE,
                    pgettext('opportunities-invoice',
                             'generated for the opportunity'),
                    [Invoice],
                ),
                (
                    constants.REL_OBJ_LINKED_INVOICE,
                    _('has resulted in the invoice'),
                    [Opportunity],
                ),
            )
            create_rtype(
                (
                    constants.REL_SUB_LINKED_QUOTE,
                    pgettext('opportunities-quote',
                             'generated for the opportunity'),
                    [Quote],
                ),
                (
                    constants.REL_OBJ_LINKED_QUOTE,
                    _('has resulted in the quote'),
                    [Opportunity],
                ),
            )
            create_rtype(
                (
                    constants.REL_SUB_CURRENT_DOC,
                    _('is the current accounting document of'),
                    [SalesOrder, Invoice, Quote],
                ),
                (
                    constants.REL_OBJ_CURRENT_DOC,
                    _('has as current accounting document'),
                    [Opportunity],
                ),
            )

        # ---------------------------
        create_sv = SettingValue.objects.get_or_create
        create_sv(key_id=setting_keys.quote_key.id, defaults={'value': False})
        create_sv(key_id=setting_keys.target_constraint_key.id,
                  defaults={'value': True})
        create_sv(key_id=setting_keys.emitter_constraint_key.id,
                  defaults={'value': True})

        # ---------------------------
        create_efilter = partial(
            EntityFilter.objects.smart_update_or_create,
            model=Opportunity,
            user='******',
        )
        build_cond = partial(
            condition_handler.RegularFieldConditionHandler.build_condition,
            model=Opportunity,
        )
        create_efilter(
            'opportunities-opportunities_won',
            name=_('Opportunities won'),
            conditions=[
                build_cond(
                    operator=operators.EqualsOperator,
                    field_name='sales_phase__won',
                    values=[True],
                ),
            ],
        )
        create_efilter(
            'opportunities-opportunities_lost',
            name=_('Opportunities lost'),
            conditions=[
                build_cond(
                    operator=operators.EqualsOperator,
                    field_name='sales_phase__lost',
                    values=[True],
                ),
            ],
        )
        create_efilter(
            'opportunities-neither_won_nor_lost_opportunities',
            name=_('Neither won nor lost opportunities'),
            conditions=[
                build_cond(
                    operator=operators.EqualsNotOperator,
                    field_name='sales_phase__won',
                    values=[True],
                ),
                build_cond(
                    operator=operators.EqualsNotOperator,
                    field_name='sales_phase__lost',
                    values=[True],
                ),
            ],
        )

        # ---------------------------
        HeaderFilter.objects.create_if_needed(
            pk=constants.DEFAULT_HFILTER_OPPORTUNITY,
            model=Opportunity,
            name=_('Opportunity view'),
            cells_desc=[
                (EntityCellRegularField, {
                    'name': 'name'
                }),
                EntityCellRelation(model=Opportunity, rtype=rt_sub_targets),
                (EntityCellRegularField, {
                    'name': 'sales_phase'
                }),
                (EntityCellRegularField, {
                    'name': 'estimated_sales'
                }),
                (EntityCellRegularField, {
                    'name': 'made_sales'
                }),
                (EntityCellRegularField, {
                    'name': 'closing_date'
                }),
            ],
        )

        # ---------------------------
        common_groups_desc = [
            {
                'name': _('Description'),
                'layout': LAYOUT_DUAL_SECOND,
                'cells': [
                    (EntityCellRegularField, {
                        'name': 'description'
                    }),
                ],
            },
            {
                'name':
                _('Custom fields'),
                'layout':
                LAYOUT_DUAL_SECOND,
                'cells': [
                    (
                        EntityCellCustomFormSpecial,
                        {
                            'name':
                            EntityCellCustomFormSpecial.REMAINING_CUSTOMFIELDS
                        },
                    ),
                ],
            },
        ]
        common_field_names = [
            'reference',
            'estimated_sales',
            'made_sales',
            'currency',
            'sales_phase',
            'chance_to_win',
            'expected_closing_date',
            'closing_date',
            'origin',
            'first_action_date',
        ]

        CustomFormConfigItem.objects.create_if_needed(
            descriptor=custom_forms.OPPORTUNITY_CREATION_CFORM,
            groups_desc=[
                {
                    'name':
                    _('General information'),
                    'layout':
                    LAYOUT_DUAL_FIRST,
                    'cells': [
                        (EntityCellRegularField, {
                            'name': 'user'
                        }),
                        (EntityCellRegularField, {
                            'name': 'name'
                        }),
                        OppEmitterSubCell().into_cell(),
                        OppTargetSubCell().into_cell(),
                        *((EntityCellRegularField, {
                            'name': fname
                        }) for fname in common_field_names),
                        (
                            EntityCellCustomFormSpecial,
                            {
                                'name':
                                EntityCellCustomFormSpecial.
                                REMAINING_REGULARFIELDS
                            },
                        ),
                    ],
                },
                *common_groups_desc,
                {
                    'name':
                    _('Properties'),
                    'cells': [
                        (
                            EntityCellCustomFormSpecial,
                            {
                                'name':
                                EntityCellCustomFormSpecial.CREME_PROPERTIES
                            },
                        ),
                    ],
                },
                {
                    'name':
                    _('Relationships'),
                    'cells': [
                        (
                            EntityCellCustomFormSpecial,
                            {
                                'name': EntityCellCustomFormSpecial.RELATIONS
                            },
                        ),
                    ],
                },
            ],
        )
        CustomFormConfigItem.objects.create_if_needed(
            descriptor=custom_forms.OPPORTUNITY_EDITION_CFORM,
            groups_desc=[
                {
                    'name':
                    _('General information'),
                    'cells': [
                        (EntityCellRegularField, {
                            'name': 'user'
                        }),
                        (EntityCellRegularField, {
                            'name': 'name'
                        }),
                        OppTargetSubCell().into_cell(),
                        *((EntityCellRegularField, {
                            'name': fname
                        }) for fname in common_field_names),
                        (
                            EntityCellCustomFormSpecial,
                            {
                                'name':
                                EntityCellCustomFormSpecial.
                                REMAINING_REGULARFIELDS
                            },
                        ),
                    ],
                },
                *common_groups_desc,
            ],
        )

        # ---------------------------
        SearchConfigItem.objects.create_if_needed(
            Opportunity,
            ['name', 'made_sales', 'sales_phase__name', 'origin__name'],
        )

        # ---------------------------
        # TODO: move to "not already_populated" section in creme2.4
        if not MenuConfigItem.objects.filter(
                entry_id__startswith='opportunities-').exists():
            container = MenuConfigItem.objects.get_or_create(
                entry_id=ContainerEntry.id,
                entry_data={'label': _('Commercial')},
                defaults={'order': 30},
            )[0]

            MenuConfigItem.objects.create(
                entry_id=menu.OpportunitiesEntry.id,
                order=10,
                parent=container,
            )

            creations = MenuConfigItem.objects.filter(
                entry_id=ContainerEntry.id,
                entry_data={
                    'label': _('+ Creation')
                },
            ).first()
            if creations is not None:
                MenuConfigItem.objects.create(
                    entry_id=menu.OpportunityCreationEntry.id,
                    order=30,
                    parent=creations,
                )

        # ---------------------------
        if not already_populated:
            create_sphase = SalesPhase.objects.create
            create_sphase(order=1, name=_('Forthcoming'))
            create_sphase(order=4,
                          name=pgettext('opportunities-sales_phase',
                                        'Abandoned'))
            create_sphase(order=5,
                          name=pgettext('opportunities-sales_phase', 'Won'),
                          won=True)
            create_sphase(order=6,
                          name=pgettext('opportunities-sales_phase', 'Lost'),
                          lost=True)
            create_sphase(order=3, name=_('Under negotiation'))
            create_sphase(order=2, name=_('In progress'))

            # ---------------------------
            create_origin = Origin.objects.create
            create_origin(name=pgettext('opportunities-origin', 'None'))
            create_origin(name=_('Web site'))
            create_origin(name=_('Mouth'))
            create_origin(name=_('Show'))
            create_origin(name=_('Direct email'))
            create_origin(name=_('Direct phone call'))
            create_origin(name=_('Employee'))
            create_origin(name=_('Partner'))
            create_origin(name=pgettext('opportunities-origin', 'Other'))

            # ---------------------------
            create_button = ButtonMenuItem.objects.create_if_needed
            create_button(
                model=Organisation,
                button=LinkedOpportunityButton,
                order=30,
            )
            create_button(
                model=Contact,
                button=LinkedOpportunityButton,
                order=30,
            )

            # ---------------------------
            LEFT = BrickDetailviewLocation.LEFT
            RIGHT = BrickDetailviewLocation.RIGHT

            build_cell = EntityCellRegularField.build
            cbci = CustomBrickConfigItem.objects.create(
                id='opportunities-complementary',
                name=_('Opportunity complementary information'),
                content_type=Opportunity,
                cells=[
                    build_cell(Opportunity, 'reference'),
                    build_cell(Opportunity, 'currency'),
                    build_cell(Opportunity, 'chance_to_win'),
                    build_cell(Opportunity, 'expected_closing_date'),
                    build_cell(Opportunity, 'closing_date'),
                    build_cell(Opportunity, 'origin'),
                    build_cell(Opportunity, 'first_action_date'),
                    build_cell(Opportunity, 'description'),
                ],
            )

            BrickDetailviewLocation.objects.multi_create(
                defaults={
                    'model': Opportunity,
                    'zone': LEFT
                },
                data=[
                    {
                        'brick': bricks.OpportunityCardHatBrick,
                        'order': 1,
                        'zone': BrickDetailviewLocation.HAT,
                    },
                    {
                        'brick': cbci.brick_id,
                        'order': 5
                    },
                    {
                        'brick': core_bricks.CustomFieldsBrick,
                        'order': 40
                    },
                    {
                        'brick': bricks.BusinessManagersBrick,
                        'order': 60
                    },
                    {
                        'brick': bricks.LinkedContactsBrick,
                        'order': 62
                    },
                    {
                        'brick': bricks.LinkedProductsBrick,
                        'order': 64
                    },
                    {
                        'brick': bricks.LinkedServicesBrick,
                        'order': 66
                    },
                    {
                        'brick': core_bricks.PropertiesBrick,
                        'order': 450
                    },
                    {
                        'brick': core_bricks.RelationsBrick,
                        'order': 500
                    },
                    {
                        'brick': bricks.OppTargetBrick,
                        'order': 1,
                        'zone': RIGHT
                    },
                    {
                        'brick': bricks.OppTotalBrick,
                        'order': 2,
                        'zone': RIGHT
                    },
                    {
                        'brick': core_bricks.HistoryBrick,
                        'order': 20,
                        'zone': RIGHT
                    },
                ],
            )

            if apps.is_installed('creme.activities'):
                logger.info(
                    'Activities app is installed'
                    ' => we use the "Future activities" & "Past activities" blocks'
                )

                from creme.activities import bricks as act_bricks

                BrickDetailviewLocation.objects.multi_create(
                    defaults={
                        'model': Opportunity,
                        'zone': RIGHT
                    },
                    data=[
                        {
                            'brick': act_bricks.FutureActivitiesBrick,
                            'order': 20
                        },
                        {
                            'brick': act_bricks.PastActivitiesBrick,
                            'order': 21
                        },
                    ],
                )

            if apps.is_installed('creme.assistants'):
                logger.info(
                    'Assistants app is installed'
                    ' => we use the assistants blocks on detail views and portal'
                )

                from creme.assistants import bricks as a_bricks

                BrickDetailviewLocation.objects.multi_create(
                    defaults={
                        'model': Opportunity,
                        'zone': RIGHT
                    },
                    data=[
                        {
                            'brick': a_bricks.TodosBrick,
                            'order': 100
                        },
                        {
                            'brick': a_bricks.MemosBrick,
                            'order': 200
                        },
                        {
                            'brick': a_bricks.AlertsBrick,
                            'order': 300
                        },
                        {
                            'brick': a_bricks.UserMessagesBrick,
                            'order': 500
                        },
                    ],
                )

            if apps.is_installed('creme.documents'):
                # logger.info('Documents app is installed
                # => we use the documents block on detail view')

                from creme.documents.bricks import LinkedDocsBrick

                BrickDetailviewLocation.objects.create_if_needed(
                    brick=LinkedDocsBrick,
                    order=600,
                    zone=RIGHT,
                    model=Opportunity,
                )

            if apps.is_installed('creme.billing'):
                logger.info('Billing app is installed'
                            ' => we use the billing blocks on detail view')

                BrickDetailviewLocation.objects.multi_create(
                    defaults={
                        'model': Opportunity,
                        'zone': LEFT
                    },
                    data=[
                        {
                            'brick': bricks.QuotesBrick,
                            'order': 70
                        },
                        {
                            'brick': bricks.SalesOrdersBrick,
                            'order': 72
                        },
                        {
                            'brick': bricks.InvoicesBrick,
                            'order': 74
                        },
                    ],
                )

            if apps.is_installed('creme.emails'):
                logger.info('Emails app is installed'
                            ' => we use the emails blocks on detail view')

                from creme.emails.bricks import MailsHistoryBrick

                BrickDetailviewLocation.objects.create_if_needed(
                    brick=MailsHistoryBrick,
                    order=600,
                    zone=RIGHT,
                    model=Opportunity,
                )

            BrickDetailviewLocation.objects.create_if_needed(
                brick=bricks.TargettingOpportunitiesBrick,
                order=16,
                zone=RIGHT,
                model=Organisation,
            )

            # ---------------------------
            if apps.is_installed('creme.reports'):
                logger.info(
                    'Reports app is installed'
                    ' => we create an Opportunity report, with 2 graphs, and related blocks'
                )
                self.create_report_n_graphes(rt_obj_emit_orga)
示例#14
0
    def populate(self):
        already_populated = RelationType.objects.filter(
            pk=constants.REL_SUB_TARGETS).exists()

        Contact = persons.get_contact_model()
        Organisation = persons.get_organisation_model()
        Product = products.get_product_model()
        Service = products.get_service_model()

        # ---------------------------
        create_rtype = RelationType.create
        rt_sub_targets = create_rtype(
            (constants.REL_SUB_TARGETS, _('targets the organisation/contact'),
             [Opportunity]),
            (constants.REL_OBJ_TARGETS, _('targeted by the opportunity'),
             [Organisation, Contact]),
            is_internal=True,
            minimal_display=(True, True),
        )[0]
        rt_obj_emit_orga = create_rtype(
            (constants.REL_SUB_EMIT_ORGA, _('has generated the opportunity'),
             [Organisation]),
            (constants.REL_OBJ_EMIT_ORGA, _('has been generated by'),
             [Opportunity]),
            is_internal=True,
            minimal_display=(True, True),
        )[1]
        create_rtype((constants.REL_SUB_LINKED_PRODUCT,
                      _('is linked to the opportunity'), [Product]),
                     (constants.REL_OBJ_LINKED_PRODUCT,
                      _('concerns the product'), [Opportunity]))
        create_rtype((constants.REL_SUB_LINKED_SERVICE,
                      _('is linked to the opportunity'), [Service]),
                     (constants.REL_OBJ_LINKED_SERVICE,
                      _('concerns the service'), [Opportunity]))
        create_rtype(
            (constants.REL_SUB_LINKED_CONTACT,
             _('involves in the opportunity'), [Contact]),
            (constants.REL_OBJ_LINKED_CONTACT, _('stages'), [Opportunity]))
        create_rtype((constants.REL_SUB_RESPONSIBLE, _('is responsible for'),
                      [Contact]),
                     (constants.REL_OBJ_RESPONSIBLE,
                      _('has as responsible contact'), [Opportunity]))

        if apps.is_installed('creme.activities'):
            logger.info(
                'Activities app is installed => an Opportunity can be the subject of an Activity'
            )

            from creme.activities.constants import REL_SUB_ACTIVITY_SUBJECT

            RelationType.objects.get(
                pk=REL_SUB_ACTIVITY_SUBJECT).add_subject_ctypes(Opportunity)

        if apps.is_installed('creme.billing'):
            logger.info(
                'Billing app is installed => we create relationships between Opportunities & billing models'
            )

            from creme.billing import get_invoice_model, get_quote_model, get_sales_order_model

            Invoice = get_invoice_model()
            Quote = get_quote_model()
            SalesOrder = get_sales_order_model()

            create_rtype(
                (constants.REL_SUB_LINKED_SALESORDER,
                 _('is associate with the opportunity'), [SalesOrder]),
                (constants.REL_OBJ_LINKED_SALESORDER,
                 _('has generated the salesorder'), [Opportunity]))
            create_rtype((constants.REL_SUB_LINKED_INVOICE,
                          _('generated for the opportunity'), [Invoice]),
                         (constants.REL_OBJ_LINKED_INVOICE,
                          _('has resulted in the invoice'), [Opportunity]))
            create_rtype((constants.REL_SUB_LINKED_QUOTE,
                          _('generated for the opportunity'), [Quote]),
                         (constants.REL_OBJ_LINKED_QUOTE,
                          _('has resulted in the quote'), [Opportunity]))
            create_rtype(
                (constants.REL_SUB_CURRENT_DOC,
                 _('is the current accounting document of'),
                 [SalesOrder, Invoice, Quote]),
                (constants.REL_OBJ_CURRENT_DOC,
                 _('has as current accounting document'), [Opportunity]))

        # ---------------------------
        create_sv = SettingValue.objects.get_or_create
        create_sv(key_id=setting_keys.quote_key.id, defaults={'value': False})
        create_sv(key_id=setting_keys.target_constraint_key.id,
                  defaults={'value': True})
        create_sv(key_id=setting_keys.emitter_constraint_key.id,
                  defaults={'value': True})

        # ---------------------------
        create_efilter = partial(EntityFilter.create,
                                 model=Opportunity,
                                 user='******')
        # create_cond    = partial(EntityFilterCondition.build_4_field, model=Opportunity)
        build_cond = partial(
            condition_handler.RegularFieldConditionHandler.build_condition,
            model=Opportunity,
        )
        create_efilter(
            'opportunities-opportunities_won',
            name=_('Opportunities won'),
            conditions=[
                # create_cond(operator=EntityFilterCondition.EQUALS,
                #             name='sales_phase__won',
                #             values=[True],
                #            ),
                build_cond(
                    operator=operators.EqualsOperator,
                    field_name='sales_phase__won',
                    values=[True],
                ),
            ],
        )
        create_efilter(
            'opportunities-opportunities_lost',
            name=_('Opportunities lost'),
            conditions=[
                # create_cond(operator=EntityFilterCondition.EQUALS,
                #             name='sales_phase__lost',
                #             values=[True],
                #            ),
                build_cond(
                    operator=operators.EqualsOperator,
                    field_name='sales_phase__lost',
                    values=[True],
                ),
            ],
        )
        create_efilter(
            'opportunities-neither_won_nor_lost_opportunities',
            name=_('Neither won nor lost opportunities'),
            conditions=[
                # create_cond(operator=EntityFilterCondition.EQUALS_NOT,
                #             name='sales_phase__won',
                #             values=[True],
                #           ),
                build_cond(
                    operator=operators.EqualsNotOperator,
                    field_name='sales_phase__won',
                    values=[True],
                ),
                # create_cond(operator=EntityFilterCondition.EQUALS_NOT,
                #             name='sales_phase__lost',
                #             values=[True],
                #            ),
                build_cond(
                    operator=operators.EqualsNotOperator,
                    field_name='sales_phase__lost',
                    values=[True],
                ),
            ],
        )

        # ---------------------------
        HeaderFilter.create(
            pk=constants.DEFAULT_HFILTER_OPPORTUNITY,
            model=Opportunity,
            name=_('Opportunity view'),
            cells_desc=[
                (EntityCellRegularField, {
                    'name': 'name'
                }),
                EntityCellRelation(model=Opportunity, rtype=rt_sub_targets),
                (EntityCellRegularField, {
                    'name': 'sales_phase'
                }),
                (EntityCellRegularField, {
                    'name': 'estimated_sales'
                }),
                (EntityCellRegularField, {
                    'name': 'made_sales'
                }),
                (EntityCellRegularField, {
                    'name': 'closing_date'
                }),
            ],
        )

        # ---------------------------
        SearchConfigItem.create_if_needed(
            Opportunity,
            ['name', 'made_sales', 'sales_phase__name', 'origin__name'])

        # ---------------------------
        if not already_populated:
            create_sphase = SalesPhase.objects.create
            create_sphase(name=_('Forthcoming'), order=1)
            create_sphase(name=pgettext('opportunities-sales_phase',
                                        'Abandoned'),
                          order=4)
            create_sphase(name=pgettext('opportunities-sales_phase', 'Won'),
                          order=5,
                          won=True)
            create_sphase(name=pgettext('opportunities-sales_phase', 'Lost'),
                          order=6,
                          lost=True)
            create_sphase(name=_('Under negotiation'), order=3)
            create_sphase(name=_('In progress'), order=2)

            # ---------------------------
            create_origin = Origin.objects.create
            create_origin(name=pgettext('opportunities-origin', 'None'))
            create_origin(name=_('Web site'))
            create_origin(name=_('Mouth'))
            create_origin(name=_('Show'))
            create_origin(name=_('Direct email'))
            create_origin(name=_('Direct phone call'))
            create_origin(name=_('Employee'))
            create_origin(name=_('Partner'))
            create_origin(name=pgettext('opportunities-origin', 'Other'))

            # ---------------------------
            create_button = ButtonMenuItem.create_if_needed
            create_button(pk='opportunities-linked_opp_button',
                          model=Organisation,
                          button=LinkedOpportunityButton,
                          order=30)  # TODO: This pk is kept for compatibility
            create_button(pk='opportunities-linked_opp_button_contact',
                          model=Contact,
                          button=LinkedOpportunityButton,
                          order=30)

            # ---------------------------
            create_bdl = partial(
                BrickDetailviewLocation.objects.create_if_needed,
                model=Opportunity)
            LEFT = BrickDetailviewLocation.LEFT
            RIGHT = BrickDetailviewLocation.RIGHT

            build_cell = EntityCellRegularField.build
            cbci = CustomBrickConfigItem.objects.create(
                id='opportunities-complementary',
                name=_('Opportunity complementary information'),
                content_type=Opportunity,
                cells=[
                    build_cell(Opportunity, 'reference'),
                    build_cell(Opportunity, 'currency'),
                    build_cell(Opportunity, 'chance_to_win'),
                    build_cell(Opportunity, 'expected_closing_date'),
                    build_cell(Opportunity, 'closing_date'),
                    build_cell(Opportunity, 'origin'),
                    build_cell(Opportunity, 'first_action_date'),
                    build_cell(Opportunity, 'description'),
                ],
            )

            create_bdl(brick=bricks.OpportunityCardHatBrick,
                       order=1,
                       zone=BrickDetailviewLocation.HAT)
            create_bdl(brick=cbci.generate_id(), order=5, zone=LEFT)
            create_bdl(brick=core_bricks.CustomFieldsBrick,
                       order=40,
                       zone=LEFT)
            create_bdl(brick=bricks.BusinessManagersBrick, order=60, zone=LEFT)
            create_bdl(brick=bricks.LinkedContactsBrick, order=62, zone=LEFT)
            create_bdl(brick=bricks.LinkedProductsBrick, order=64, zone=LEFT)
            create_bdl(brick=bricks.LinkedServicesBrick, order=66, zone=LEFT)
            create_bdl(brick=core_bricks.PropertiesBrick, order=450, zone=LEFT)
            create_bdl(brick=core_bricks.RelationsBrick, order=500, zone=LEFT)
            create_bdl(brick=bricks.OppTargetBrick, order=1, zone=RIGHT)
            create_bdl(brick=bricks.OppTotalBrick, order=2, zone=RIGHT)
            create_bdl(brick=core_bricks.HistoryBrick, order=20, zone=RIGHT)

            if apps.is_installed('creme.activities'):
                logger.info(
                    'Activities app is installed => we use the "Future activities" & "Past activities" blocks'
                )

                from creme.activities import bricks as act_bricks

                create_bdl(brick=act_bricks.FutureActivitiesBrick,
                           order=20,
                           zone=RIGHT)
                create_bdl(brick=act_bricks.PastActivitiesBrick,
                           order=21,
                           zone=RIGHT)

            if apps.is_installed('creme.assistants'):
                logger.info(
                    'Assistants app is installed => we use the assistants blocks on detail views and portal'
                )

                from creme.assistants import bricks as assistants_bricks

                create_bdl(brick=assistants_bricks.TodosBrick,
                           order=100,
                           zone=RIGHT)
                create_bdl(brick=assistants_bricks.MemosBrick,
                           order=200,
                           zone=RIGHT)
                create_bdl(brick=assistants_bricks.AlertsBrick,
                           order=300,
                           zone=RIGHT)
                create_bdl(brick=assistants_bricks.UserMessagesBrick,
                           order=500,
                           zone=RIGHT)

            if apps.is_installed('creme.documents'):
                # logger.info('Documents app is installed => we use the documents block on detail view')

                from creme.documents.bricks import LinkedDocsBrick

                create_bdl(brick=LinkedDocsBrick, order=600, zone=RIGHT)

            if apps.is_installed('creme.billing'):
                logger.info(
                    'Billing app is installed => we use the billing blocks on detail view'
                )

                create_bdl(brick=bricks.QuotesBrick, order=70, zone=LEFT)
                create_bdl(brick=bricks.SalesOrdersBrick, order=72, zone=LEFT)
                create_bdl(brick=bricks.InvoicesBrick, order=74, zone=LEFT)

            if apps.is_installed('creme.emails'):
                logger.info(
                    'Emails app is installed => we use the emails blocks on detail view'
                )

                from creme.emails.bricks import MailsHistoryBrick

                create_bdl(brick=MailsHistoryBrick, order=600, zone=RIGHT)

            create_bdl(brick=bricks.TargettingOpportunitiesBrick,
                       order=16,
                       zone=RIGHT,
                       model=Organisation)

            # ---------------------------
            if apps.is_installed('creme.reports'):
                logger.info(
                    'Reports app is installed => we create an Opportunity report, with 2 graphs, and related blocks'
                )
                self.create_report_n_graphes(rt_obj_emit_orga)