0

While following the installation process for Saleor headless e-commerce, the Python Weasyprint package fails to load the gobject-2.0.0 dependency which I have already installed on my machine using Macport.

Below is the source code showing where the error is emitting from after starting the Django server. The file holds utility functions for generating invoices for the plugin file.

utils.py

import os
import re
from datetime import datetime
from decimal import Decimal

import pytz
from django.conf import settings
from django.template.loader import get_template
from prices import Money
from weasyprint import HTML # <----------- This what is emitting the error because the  
# package can't find the needed dependency.

from ...giftcard import GiftCardEvents
from ...giftcard.models import GiftCardEvent
from ...invoice.models import Invoice

MAX_PRODUCTS_WITH_TABLE = 3
MAX_PRODUCTS_WITHOUT_TABLE = 4
MAX_PRODUCTS_PER_PAGE = 13


def make_full_invoice_number(number=None, month=None, year=None):
    now = datetime.now()
    current_month = int(now.strftime("%m"))
    current_year = int(now.strftime("%Y"))
    month_and_year = now.strftime("%m/%Y")

    if month == current_month and year == current_year:
        new_number = (number or 0) + 1
        return f"{new_number}/{month_and_year}"
    return f"1/{month_and_year}"


def parse_invoice_dates(number: str):
    match = re.match(r"^(\d+)\/(\d+)\/(\d+)", number)
    if not match:
        raise ValueError("Unrecognized invoice number format")
    return int(match.group(1)), int(match.group(2)), int(match.group(3))


def generate_invoice_number():
    last_invoice = Invoice.objects.filter(number__isnull=False).last()
    if not last_invoice or not last_invoice.number:
        return make_full_invoice_number()

    try:
        number, month, year = parse_invoice_dates(last_invoice.number)
        return make_full_invoice_number(number, month, year)
    except (IndexError, ValueError, AttributeError):
        return make_full_invoice_number()


def chunk_products(products, product_limit):
    """Split products to list of chunks.

    Each chunk represents products per page, product_limit defines chunk size.
    """
    chunks = []
    for i in range(0, len(products), product_limit):
        limit = i + product_limit
        chunks.append(products[i:limit])
    return chunks


def get_product_limit_first_page(products):
    if len(products) < MAX_PRODUCTS_WITHOUT_TABLE:
        return MAX_PRODUCTS_WITH_TABLE

    return MAX_PRODUCTS_WITHOUT_TABLE


def get_gift_cards_payment_amount(order):
    events = GiftCardEvent.objects.filter(
        type=GiftCardEvents.USED_IN_ORDER, order_id=order.id
    )
    total_paid = Decimal(0)
    for event in events:
        balance = event.parameters["balance"]
        total_paid += Decimal(balance["old_current_balance"]) - Decimal(
            balance["current_balance"]
        )
    return Money(total_paid, order.currency)


def generate_invoice_pdf(invoice): # <------- The function calling the HTML module from 
# weasyprint

    font_path = os.path.join(
        settings.PROJECT_ROOT, "templates", "invoices", "inter.ttf"
    )

    all_products = invoice.order.lines.all()

    product_limit_first_page = get_product_limit_first_page(all_products)

    products_first_page = all_products[:product_limit_first_page]
    rest_of_products = chunk_products(
        all_products[product_limit_first_page:], MAX_PRODUCTS_PER_PAGE
    )
    order = invoice.order
    gift_cards_payment = get_gift_cards_payment_amount(order)
    creation_date = datetime.now(tz=pytz.utc)
    rendered_template = get_template("invoices/invoice.html").render(
        {
            "invoice": invoice,
            "creation_date": creation_date.strftime("%d %b %Y"),
            "order": order,
            "gift_cards_payment": gift_cards_payment,
            "font_path": f"file://{font_path}",
            "products_first_page": products_first_page,
            "rest_of_products": rest_of_products,
        }
    )
    return HTML(string=rendered_template).write_pdf(), creation_date

plugins.py

from typing import Any, Optional
from uuid import uuid4

from django.core.files.base import ContentFile
from django.utils.text import slugify

from ...core import JobStatus
from ...invoice.models import Invoice
from ...order.models import Order
from ..base_plugin import BasePlugin
from .utils import generate_invoice_number, generate_invoice_pdf


class InvoicingPlugin(BasePlugin):
    PLUGIN_ID = "mirumee.invoicing"
    PLUGIN_NAME = "Invoicing"
    DEFAULT_ACTIVE = True
    PLUGIN_DESCRIPTION = "Built-in saleor plugin that handles invoice creation."
    CONFIGURATION_PER_CHANNEL = False

    def invoice_request(
        self,
        order: "Order",
        invoice: "Invoice",
        number: Optional[str],
        previous_value: Any,
    ) -> Any:
        invoice_number = generate_invoice_number()
        invoice.update_invoice(number=invoice_number)
        file_content, creation_date = generate_invoice_pdf(invoice)
        invoice.created = creation_date
        slugified_invoice_number = slugify(invoice_number)
        invoice.invoice_file.save(
            f"invoice-{slugified_invoice_number}-order-{order.id}-{uuid4()}.pdf",
            ContentFile(file_content),  # type: ignore
        )
        invoice.status = JobStatus.SUCCESS
        invoice.save(
            update_fields=[
                "created_at",
                "number",
                "invoice_file",
                "status",
                "updated_at",
            ]
        )
        return invoice

To fix the issue, I followed this instruction of creating a symlink which I did and pointed to it in the path environment of my machine yet it didn't fix the issue. Does that mean that Django isn't checking for the depenecy using the path environment?

It's also worth noting that an installation of Python and weasyprint using Homebrew would fix the issue but I don't use home because I'm MacOS Catalina 10.15 which isn't supported anymore thus the version for it is unstable to use.

I know the dependency is on my machine but it's been difficult to point to it? What am I doing wrong?

I've been on this for days!

Romeo
  • 740
  • 1
  • 5
  • 21

1 Answers1

1

After many days, it turned out this is a normal issue faced when using the Python Weasyprint package. This occurs when you install both Python and the required system dependencies using different installers. In my case, I used a system installer for Python and Macport installer for the dependencies and that's what caused the issue. Fortunately, answers already exist to solve the issue but I found this one particularly useful.

Romeo
  • 740
  • 1
  • 5
  • 21