Skip to content

Payment

hypha.apply.projects.models.payment

SUBMITTED module-attribute

SUBMITTED = 'submitted'

RESUBMITTED module-attribute

RESUBMITTED = 'resubmitted'

CHANGES_REQUESTED_BY_STAFF module-attribute

CHANGES_REQUESTED_BY_STAFF = 'changes_requested_staff'

CHANGES_REQUESTED_BY_FINANCE module-attribute

CHANGES_REQUESTED_BY_FINANCE = 'changes_requested_finance_1'

CHANGES_REQUESTED_BY_FINANCE_2 module-attribute

CHANGES_REQUESTED_BY_FINANCE_2 = 'changes_requested_finance_2'

APPROVED_BY_STAFF module-attribute

APPROVED_BY_STAFF = 'approved_by_staff'

APPROVED_BY_FINANCE module-attribute

APPROVED_BY_FINANCE = 'approved_by_finance_1'

APPROVED_BY_FINANCE_2 module-attribute

APPROVED_BY_FINANCE_2 = 'approved_by_finance_2'

PAID module-attribute

PAID = 'paid'

PAYMENT_FAILED module-attribute

PAYMENT_FAILED = 'payment_failed'

DECLINED module-attribute

DECLINED = 'declined'

INVOICE_STATUS_CHOICES module-attribute

INVOICE_STATUS_CHOICES = [(SUBMITTED, gettext_lazy('Submitted')), (RESUBMITTED, gettext_lazy('Resubmitted')), (CHANGES_REQUESTED_BY_STAFF, gettext_lazy('Changes requested by staff')), (CHANGES_REQUESTED_BY_FINANCE, gettext_lazy('Changes requested by finance')), (CHANGES_REQUESTED_BY_FINANCE_2, gettext_lazy('Changes requested by finance 2')), (APPROVED_BY_STAFF, gettext_lazy('Approved by staff')), (APPROVED_BY_FINANCE, gettext_lazy('Approved by finance')), (APPROVED_BY_FINANCE_2, gettext_lazy('Approved by finance 2')), (PAID, gettext_lazy('Paid')), (PAYMENT_FAILED, gettext_lazy('Payment failed')), (DECLINED, gettext_lazy('Declined'))]

INVOICE_TRANISTION_TO_RESUBMITTED module-attribute

INVOICE_STATUS_PM_CHOICES module-attribute

INVOICE_STATUS_PM_CHOICES = [CHANGES_REQUESTED_BY_STAFF, APPROVED_BY_STAFF, DECLINED]

INVOICE_STATUS_FINANCE_1_CHOICES module-attribute

INVOICE_STATUS_FINANCE_2_CHOICES module-attribute

INVOICE_STATUS_FINANCE_2_CHOICES = []

InvoiceQueryset

Bases: QuerySet

in_progress

in_progress()
Source code in hypha/apply/projects/models/payment.py
def in_progress(self):
    return self.exclude(status__in=[DECLINED, PAID])

approved_by_staff

approved_by_staff()
Source code in hypha/apply/projects/models/payment.py
def approved_by_staff(self):
    return self.filter(status=APPROVED_BY_STAFF)

approved_by_finance_1

approved_by_finance_1()
Source code in hypha/apply/projects/models/payment.py
def approved_by_finance_1(self):
    return self.filter(status=APPROVED_BY_FINANCE)

approved_by_finance_2

approved_by_finance_2()
Source code in hypha/apply/projects/models/payment.py
def approved_by_finance_2(self):
    return self.filter(status=APPROVED_BY_FINANCE_2)

waiting_to_convert

waiting_to_convert()
Source code in hypha/apply/projects/models/payment.py
def waiting_to_convert(self):
    if settings.INVOICE_EXTENDED_WORKFLOW:
        return self.filter(status=APPROVED_BY_FINANCE_2)
    return self.filter(status=APPROVED_BY_FINANCE)

for_finance_1

for_finance_1()
Source code in hypha/apply/projects/models/payment.py
def for_finance_1(self):
    if settings.INVOICE_EXTENDED_WORKFLOW:
        return self.filter(
            status__in=[APPROVED_BY_STAFF, CHANGES_REQUESTED_BY_FINANCE_2]
        )
    return self.filter(status__in=[APPROVED_BY_STAFF, APPROVED_BY_FINANCE])

for_finance_2

for_finance_2()
Source code in hypha/apply/projects/models/payment.py
def for_finance_2(self):
    if settings.INVOICE_EXTENDED_WORKFLOW:
        return self.filter(status__in=[APPROVED_BY_FINANCE, APPROVED_BY_FINANCE_2])
    return []

rejected

rejected()
Source code in hypha/apply/projects/models/payment.py
def rejected(self):
    return self.filter(status=DECLINED)

not_rejected

not_rejected()
Source code in hypha/apply/projects/models/payment.py
def not_rejected(self):
    return self.exclude(status=DECLINED)

total_value

total_value(field)
Source code in hypha/apply/projects/models/payment.py
def total_value(self, field):
    return self.aggregate(
        total=Coalesce(Sum(field), Value(0), output_field=models.DecimalField())
    )["total"]

paid_value

paid_value()
Source code in hypha/apply/projects/models/payment.py
def paid_value(self):
    return self.filter(status=PAID).total_value("paid_value")

unpaid_value

unpaid_value()
Source code in hypha/apply/projects/models/payment.py
def unpaid_value(self):
    return self.filter(~Q(status=PAID)).total_value("paid_value")

InvoiceDeliverable

Bases: Model

deliverable class-attribute instance-attribute

deliverable = ForeignKey('Deliverable', on_delete=CASCADE, related_name='deliverables')

quantity class-attribute instance-attribute

quantity = IntegerField(help_text=gettext_lazy('Quantity Selected on an Invoice'), default=0)

wagtail_reference_index_ignore class-attribute instance-attribute

wagtail_reference_index_ignore = True

get_absolute_api_url

get_absolute_api_url()
Source code in hypha/apply/projects/models/payment.py
def get_absolute_api_url(self):
    return reverse(
        "api:v1:remove-deliverables", kwargs={"pk": self.pk, "invoice_pk": self.pk}
    )

Invoice

Bases: Model

project class-attribute instance-attribute

project = ForeignKey('Project', on_delete=CASCADE, related_name='invoices')

by class-attribute instance-attribute

by = ForeignKey(AUTH_USER_MODEL, on_delete=CASCADE, related_name='invoices')

paid_value class-attribute instance-attribute

paid_value = DecimalField(max_digits=10, decimal_places=2, validators=[MinValueValidator(Decimal('0.01'))], null=True)

document class-attribute instance-attribute

document = FileField(upload_to=invoice_path, storage=PrivateStorage())

requested_at class-attribute instance-attribute

requested_at = DateTimeField(auto_now_add=True)

message_for_pm class-attribute instance-attribute

message_for_pm = TextField(blank=True, verbose_name=gettext_lazy('Message'))

comment class-attribute instance-attribute

comment = TextField(blank=True)

invoice_number class-attribute instance-attribute

invoice_number = CharField(max_length=50, null=True, verbose_name=gettext_lazy('Invoice number'))

invoice_amount class-attribute instance-attribute

invoice_amount = DecimalField(max_digits=10, decimal_places=2, validators=[MinValueValidator(Decimal('0.01'))], null=True, verbose_name=gettext_lazy('Invoice amount'))

status class-attribute instance-attribute

status = FSMField(default=SUBMITTED, choices=INVOICE_STATUS_CHOICES)

deliverables class-attribute instance-attribute

deliverables = ManyToManyField('InvoiceDeliverable', related_name='invoices')

objects class-attribute instance-attribute

objects = as_manager()

wagtail_reference_index_ignore class-attribute instance-attribute

wagtail_reference_index_ignore = True

has_changes_requested property

has_changes_requested

status_display property

status_display

vendor_document_number property

vendor_document_number

Vendor document number is a required field to create invoices in IntAcct.

Formatting should be HP###### i.e. HP000001 and so on.

value property

value

deliverables_total_amount property

deliverables_total_amount

filename property

filename

transition_invoice_to_resubmitted

transition_invoice_to_resubmitted()

Tranistion invoice to resubmitted status. This method generally gets used on invoice edit.

Source code in hypha/apply/projects/models/payment.py
@transition(
    field=status, source=INVOICE_TRANISTION_TO_RESUBMITTED, target=RESUBMITTED
)
def transition_invoice_to_resubmitted(self):
    """
    Tranistion invoice to resubmitted status.
    This method generally gets used on invoice edit.
    """
    pass

can_user_delete

can_user_delete(user)
Source code in hypha/apply/projects/models/payment.py
def can_user_delete(self, user):
    if user.is_applicant or user.is_apply_staff:
        if self.status in (SUBMITTED):
            return True

    return False

can_user_edit

can_user_edit(user)

Check when an user can edit an invoice. Only applicant and staff have permission to edit invoice based on its current status.

Source code in hypha/apply/projects/models/payment.py
def can_user_edit(self, user):
    """
    Check when an user can edit an invoice.
    Only applicant and staff have permission to edit invoice based on its current status.
    """
    if user.is_applicant:
        if self.status in {SUBMITTED, CHANGES_REQUESTED_BY_STAFF, RESUBMITTED}:
            return True

    if user.is_apply_staff:
        if self.status in {SUBMITTED, RESUBMITTED, CHANGES_REQUESTED_BY_FINANCE}:
            return True

    return False

can_user_change_status

can_user_change_status(user)

Check user roles that can tranistion invoice status based on the current status.

Source code in hypha/apply/projects/models/payment.py
def can_user_change_status(self, user):
    """
    Check user roles that can tranistion invoice status based on the current status.
    """
    if not (
        user.is_contracting
        or user.is_apply_staff
        or user.is_finance_level_1
        or user.is_finance_level_2
    ):
        return False  # Users can't change status

    if self.status in {DECLINED}:
        return False

    if user.is_contracting:
        if self.status in {SUBMITTED, CHANGES_REQUESTED_BY_STAFF, RESUBMITTED}:
            return True

    if user.is_apply_staff:
        if self.status in {
            SUBMITTED,
            RESUBMITTED,
            CHANGES_REQUESTED_BY_STAFF,
            CHANGES_REQUESTED_BY_FINANCE,
        }:
            return True

    if user.is_finance_level_1:
        if settings.INVOICE_EXTENDED_WORKFLOW:
            if self.status in {APPROVED_BY_STAFF, CHANGES_REQUESTED_BY_FINANCE_2}:
                return True
        else:
            if self.status in {
                APPROVED_BY_STAFF,
                APPROVED_BY_FINANCE,
                PAID,
                PAYMENT_FAILED,
            }:
                return True

    if user.is_finance_level_2:
        if self.status in {
            APPROVED_BY_FINANCE,
            APPROVED_BY_FINANCE_2,
            PAID,
            PAYMENT_FAILED,
        }:
            return True

    return False

can_user_edit_deliverables

can_user_edit_deliverables(user)
Source code in hypha/apply/projects/models/payment.py
def can_user_edit_deliverables(self, user):
    if not (
        user.is_apply_staff or user.is_finance_level_1 or user.is_finance_level_2
    ):
        return False
    if user.is_apply_staff:
        if self.status in {SUBMITTED, RESUBMITTED, CHANGES_REQUESTED_BY_FINANCE}:
            return True
    if user.is_finance_level_1:
        if self.status in {APPROVED_BY_STAFF}:
            return True
        elif settings.INVOICE_EXTENDED_WORKFLOW and self.status in [
            CHANGES_REQUESTED_BY_FINANCE_2
        ]:
            return True
    if user.is_finance_level_2:
        if self.status in {APPROVED_BY_FINANCE}:
            return True
    return False

get_absolute_url

get_absolute_url()
Source code in hypha/apply/projects/models/payment.py
def get_absolute_url(self):
    return reverse(
        "apply:projects:invoice-detail",
        kwargs={"pk": self.project.pk, "invoice_pk": self.pk},
    )

SupportingDocument

Bases: Model

wagtail_reference_index_ignore class-attribute instance-attribute

wagtail_reference_index_ignore = True

document class-attribute instance-attribute

document = FileField(upload_to='supporting_documents', storage=PrivateStorage())

invoice class-attribute instance-attribute

invoice = ForeignKey(Invoice, on_delete=CASCADE, related_name='supporting_documents')

filename property

filename

invoice_status_user_choices

invoice_status_user_choices(user)
Source code in hypha/apply/projects/models/payment.py
def invoice_status_user_choices(user):
    if user.is_finance_level_2:
        return INVOICE_STATUS_FINANCE_2_CHOICES
    if user.is_finance_level_1:
        return INVOICE_STATUS_FINANCE_1_CHOICES
    if user.is_apply_staff:
        return INVOICE_STATUS_PM_CHOICES
    return []

invoice_path

invoice_path(instance, filename)
Source code in hypha/apply/projects/models/payment.py
def invoice_path(instance, filename):
    return f"projects/{instance.project_id}/payment_invoices/{filename}"