def clean(self):
        data = self.cleaned_data
        if not data.get('is_business'):
            data['company'] = ''
            data['vat_id'] = ''
        if data.get('is_business') and not is_eu_country(data.get('country')):
            data['vat_id'] = ''
        if self.event.settings.invoice_address_required:
            if data.get('is_business') and not data.get('company'):
                raise ValidationError(_('You need to provide a company name.'))
            if not data.get('is_business') and not data.get('name_parts'):
                raise ValidationError(_('You need to provide your name.'))

        if 'vat_id' in self.changed_data or not data.get('vat_id'):
            self.instance.vat_id_validated = False

        if data.get('city') and data.get('country') and str(data['country']) in COUNTRIES_WITH_STATE_IN_ADDRESS:
            if not data.get('state'):
                self.add_error('state', _('This field is required.'))

        self.instance.name_parts = data.get('name_parts')

        if all(
                not v for k, v in data.items() if k not in ('is_business', 'country', 'name_parts')
        ) and len(data.get('name_parts', {})) == 1:
            # Do not save the country if it is the only field set -- we don't know the user even checked it!
            self.cleaned_data['country'] = ''
        if self.validate_vat_id and self.instance.vat_id_validated and 'vat_id' not in self.changed_data:
            pass
        elif self.validate_vat_id and data.get('is_business') and is_eu_country(data.get('country')) and data.get('vat_id'):
            if data.get('vat_id')[:2] != cc_to_vat_prefix(str(data.get('country'))):
                raise ValidationError(_('Your VAT ID does not match the selected country.'))
            try:
                result = vat_moss.id.validate(data.get('vat_id'))
                if result:
                    country_code, normalized_id, company_name = result
                    self.instance.vat_id_validated = True
                    self.instance.vat_id = normalized_id
            except (vat_moss.errors.InvalidError, ValueError):
                raise ValidationError(_('This VAT ID is not valid. Please re-check your input.'))
            except vat_moss.errors.WebServiceUnavailableError:
                logger.exception('VAT ID checking failed for country {}'.format(data.get('country')))
                self.instance.vat_id_validated = False
                if self.request and self.vat_warning:
                    messages.warning(self.request, _('Your VAT ID could not be checked, as the VAT checking service of '
                                                     'your country is currently not available. We will therefore '
                                                     'need to charge VAT on your invoice. You can get the tax amount '
                                                     'back via the VAT reimbursement process.'))
            except (vat_moss.errors.WebServiceError, HTTPError):
                logger.exception('VAT ID checking failed for country {}'.format(data.get('country')))
                self.instance.vat_id_validated = False
                if self.request and self.vat_warning:
                    messages.warning(self.request, _('Your VAT ID could not be checked, as the VAT checking service of '
                                                     'your country returned an incorrect result. We will therefore '
                                                     'need to charge VAT on your invoice. Please contact support to '
                                                     'resolve this manually.'))
        else:
            self.instance.vat_id_validated = False
Example #2
0
def validate_vat_id(vat_id, country_code):
    country_code = str(country_code)
    if is_eu_country(country_code):
        return _validate_vat_id_EU(vat_id, country_code)
    elif country_code == 'CH':
        return _validate_vat_id_CH(vat_id, country_code)

    raise VATIDTemporaryError(
        f'VAT ID should not be entered for country {country_code}')
Example #3
0
    def __init__(self, *args, **kwargs):
        self.event = event = kwargs.pop('event')
        self.request = kwargs.pop('request', None)
        self.validate_vat_id = kwargs.pop('validate_vat_id')
        self.all_optional = kwargs.pop('all_optional', False)

        kwargs.setdefault('initial', {})
        if not kwargs.get('instance') or not kwargs['instance'].country:
            kwargs['initial']['country'] = guess_country(self.event)

        super().__init__(*args, **kwargs)
        if not event.settings.invoice_address_vatid:
            del self.fields['vat_id']

        self.fields['country'].choices = CachedCountries()

        c = [('', pgettext_lazy('address', 'Select state'))]
        fprefix = self.prefix + '-' if self.prefix else ''
        cc = None
        if fprefix + 'country' in self.data:
            cc = str(self.data[fprefix + 'country'])
        elif 'country' in self.initial:
            cc = str(self.initial['country'])
        elif self.instance and self.instance.country:
            cc = str(self.instance.country)
        if cc and cc in COUNTRIES_WITH_STATE_IN_ADDRESS:
            types, form = COUNTRIES_WITH_STATE_IN_ADDRESS[cc]
            statelist = [
                s for s in pycountry.subdivisions.get(country_code=cc)
                if s.type in types
            ]
            c += sorted([(s.code[3:], s.name) for s in statelist],
                        key=lambda s: s[1])
        elif fprefix + 'state' in self.data:
            self.data = self.data.copy()
            del self.data[fprefix + 'state']

        self.fields['state'] = forms.ChoiceField(
            label=pgettext_lazy('address', 'State'),
            required=False,
            choices=c,
            widget=forms.Select(attrs={
                'autocomplete': 'address-level1',
            }),
        )
        self.fields['state'].widget.is_required = True

        # Without JavaScript the VAT ID field is not hidden, so we empty the field if a country outside the EU is selected.
        if cc and not is_eu_country(cc) and fprefix + 'vat_id' in self.data:
            self.data = self.data.copy()
            del self.data[fprefix + 'vat_id']

        if not event.settings.invoice_address_required or self.all_optional:
            for k, f in self.fields.items():
                f.required = False
                f.widget.is_required = False
                if 'required' in f.widget.attrs:
                    del f.widget.attrs['required']
        elif event.settings.invoice_address_company_required and not self.all_optional:
            self.initial['is_business'] = True

            self.fields['is_business'].widget = BusinessBooleanRadio(
                require_business=True)
            self.fields['company'].required = True
            self.fields['company'].widget.is_required = True
            self.fields['company'].widget.attrs['required'] = 'required'
            del self.fields['company'].widget.attrs['data-display-dependency']

        self.fields['name_parts'] = NamePartsFormField(
            max_length=255,
            required=event.settings.invoice_name_required
            and not self.all_optional,
            scheme=event.settings.name_scheme,
            titles=event.settings.name_scheme_titles,
            label=_('Name'),
            initial=(self.instance.name_parts
                     if self.instance else self.instance.name_parts),
        )
        if event.settings.invoice_address_required and not event.settings.invoice_address_company_required and not self.all_optional:
            if not event.settings.invoice_name_required:
                self.fields['name_parts'].widget.attrs[
                    'data-required-if'] = '#id_is_business_0'
            self.fields['name_parts'].widget.attrs[
                'data-no-required-attr'] = '1'
            self.fields['company'].widget.attrs[
                'data-required-if'] = '#id_is_business_1'

        if not event.settings.invoice_address_beneficiary:
            del self.fields['beneficiary']

        if event.settings.invoice_address_custom_field:
            self.fields[
                'custom_field'].label = event.settings.invoice_address_custom_field
        else:
            del self.fields['custom_field']

        for k, v in self.fields.items():
            if v.widget.attrs.get('autocomplete') or k == 'name_parts':
                v.widget.attrs[
                    'autocomplete'] = 'section-invoice billing ' + v.widget.attrs.get(
                        'autocomplete', '')
Example #4
0
def build_invoice(invoice: Invoice) -> Invoice:
    invoice.locale = invoice.event.settings.get('invoice_language',
                                                invoice.event.settings.locale)
    if invoice.locale == '__user__':
        invoice.locale = invoice.order.locale or invoice.event.settings.locale

    lp = invoice.order.payments.last()

    with language(invoice.locale):
        invoice.invoice_from = invoice.event.settings.get(
            'invoice_address_from')
        invoice.invoice_from_name = invoice.event.settings.get(
            'invoice_address_from_name')
        invoice.invoice_from_zipcode = invoice.event.settings.get(
            'invoice_address_from_zipcode')
        invoice.invoice_from_city = invoice.event.settings.get(
            'invoice_address_from_city')
        invoice.invoice_from_country = invoice.event.settings.get(
            'invoice_address_from_country')
        invoice.invoice_from_tax_id = invoice.event.settings.get(
            'invoice_address_from_tax_id')
        invoice.invoice_from_vat_id = invoice.event.settings.get(
            'invoice_address_from_vat_id')

        introductory = invoice.event.settings.get('invoice_introductory_text',
                                                  as_type=LazyI18nString)
        additional = invoice.event.settings.get('invoice_additional_text',
                                                as_type=LazyI18nString)
        footer = invoice.event.settings.get('invoice_footer_text',
                                            as_type=LazyI18nString)
        if lp and lp.payment_provider:
            if 'payment' in inspect.signature(
                    lp.payment_provider.render_invoice_text).parameters:
                payment = str(
                    lp.payment_provider.render_invoice_text(invoice.order, lp))
            else:
                payment = str(
                    lp.payment_provider.render_invoice_text(invoice.order))
        else:
            payment = ""
        if invoice.event.settings.invoice_include_expire_date and invoice.order.status == Order.STATUS_PENDING:
            if payment:
                payment += "<br />"
            payment += pgettext(
                "invoice",
                "Please complete your payment before {expire_date}.").format(
                    expire_date=date_format(invoice.order.expires,
                                            "SHORT_DATE_FORMAT"))

        invoice.introductory_text = str(introductory).replace('\n', '<br />')
        invoice.additional_text = str(additional).replace('\n', '<br />')
        invoice.footer_text = str(footer)
        invoice.payment_provider_text = str(payment).replace('\n', '<br />')

        try:
            ia = invoice.order.invoice_address
            addr_template = pgettext(
                "invoice", """{i.company}
{i.name}
{i.street}
{i.zipcode} {i.city} {state}
{country}""")
            invoice.invoice_to = "\n".join(
                a.strip() for a in addr_template.format(
                    i=ia,
                    country=ia.country.name if ia.country else ia.country_old,
                    state=ia.state_for_address).split("\n") if a.strip())
            invoice.internal_reference = ia.internal_reference
            invoice.custom_field = ia.custom_field
            invoice.invoice_to_company = ia.company
            invoice.invoice_to_name = ia.name
            invoice.invoice_to_street = ia.street
            invoice.invoice_to_zipcode = ia.zipcode
            invoice.invoice_to_city = ia.city
            invoice.invoice_to_country = ia.country
            invoice.invoice_to_state = ia.state
            invoice.invoice_to_beneficiary = ia.beneficiary

            if ia.vat_id:
                invoice.invoice_to += "\n" + pgettext("invoice",
                                                      "VAT-ID: %s") % ia.vat_id
                invoice.invoice_to_vat_id = ia.vat_id

            cc = str(ia.country)

            if cc in EU_CURRENCIES and EU_CURRENCIES[
                    cc] != invoice.event.currency and invoice.event.settings.invoice_eu_currencies:
                invoice.foreign_currency_display = EU_CURRENCIES[cc]

                if settings.FETCH_ECB_RATES:
                    gs = GlobalSettingsObject()
                    rates_date = gs.settings.get('ecb_rates_date',
                                                 as_type=date)
                    rates_dict = gs.settings.get('ecb_rates_dict',
                                                 as_type=dict)
                    convert = (rates_date and rates_dict and rates_date >
                               (now() - timedelta(days=7)).date()
                               and invoice.event.currency in rates_dict and
                               invoice.foreign_currency_display in rates_dict)
                    if convert:
                        invoice.foreign_currency_rate = (
                            Decimal(
                                rates_dict[invoice.foreign_currency_display]) /
                            Decimal(
                                rates_dict[invoice.event.currency])).quantize(
                                    Decimal('0.0001'), ROUND_HALF_UP)
                        invoice.foreign_currency_rate_date = rates_date

        except InvoiceAddress.DoesNotExist:
            ia = None
            invoice.invoice_to = ""

        invoice.file = None
        invoice.save()
        invoice.lines.all().delete()

        positions = list(
            invoice.order.positions.select_related(
                'addon_to', 'item', 'tax_rule', 'subevent',
                'variation').annotate(
                    addon_c=Count('addons')).prefetch_related(
                        'answers',
                        'answers__question').order_by('positionid', 'id'))

        reverse_charge = False

        positions.sort(key=lambda p: p.sort_key)
        for i, p in enumerate(positions):
            if not invoice.event.settings.invoice_include_free and p.price == Decimal(
                    '0.00') and not p.addon_c:
                continue

            desc = str(p.item.name)
            if p.variation:
                desc += " - " + str(p.variation.value)
            if p.addon_to_id:
                desc = "  + " + desc
            if invoice.event.settings.invoice_attendee_name and p.attendee_name:
                desc += "<br />" + pgettext(
                    "invoice", "Attendee: {name}").format(name=p.attendee_name)
            for recv, resp in invoice_line_text.send(sender=invoice.event,
                                                     position=p):
                if resp:
                    desc += "<br/>" + resp

            for answ in p.answers.all():
                if not answ.question.print_on_invoice:
                    continue
                desc += "<br />{}{} {}".format(
                    answ.question.question,
                    "" if str(answ.question.question).endswith("?") else ":",
                    str(answ))

            if invoice.event.has_subevents:
                desc += "<br />" + pgettext("subevent", "Date: {}").format(
                    p.subevent)
            InvoiceLine.objects.create(
                position=i,
                invoice=invoice,
                description=desc,
                gross_value=p.price,
                tax_value=p.tax_value,
                subevent=p.subevent,
                event_date_from=(p.subevent.date_from
                                 if p.subevent else invoice.event.date_from),
                tax_rate=p.tax_rate,
                tax_name=p.tax_rule.name if p.tax_rule else '')

            if p.tax_rule and p.tax_rule.is_reverse_charge(
                    ia) and p.price and not p.tax_value:
                reverse_charge = True

        if reverse_charge:
            if invoice.additional_text:
                invoice.additional_text += "<br /><br />"
            if is_eu_country(invoice.invoice_to_country):
                invoice.additional_text += pgettext(
                    "invoice",
                    "Reverse Charge: According to Article 194, 196 of Council Directive 2006/112/EEC, VAT liability "
                    "rests with the service recipient.")
            else:
                invoice.additional_text += pgettext(
                    "invoice",
                    "VAT liability rests with the service recipient.")
            invoice.reverse_charge = True
            invoice.save()

        offset = len(positions)
        for i, fee in enumerate(invoice.order.fees.all()):
            if fee.fee_type == OrderFee.FEE_TYPE_OTHER and fee.description:
                fee_title = fee.description
            else:
                fee_title = _(fee.get_fee_type_display())
                if fee.description:
                    fee_title += " - " + fee.description
            InvoiceLine.objects.create(
                position=i + offset,
                invoice=invoice,
                description=fee_title,
                gross_value=fee.value,
                tax_value=fee.tax_value,
                tax_rate=fee.tax_rate,
                tax_name=fee.tax_rule.name if fee.tax_rule else '')

        return invoice