跳转到主要内容

为django-oscar管理账户

项目描述

“管理账户”是指可以借记和贷记的资金分配。本包为与电子商务框架Oscar一起使用提供了管理账户功能。

账户可用于实现各种有趣的功能,包括

  • 礼品卡

  • 网络账户

  • 忠诚度计划

基本上,任何涉及在封闭系统中跟踪资金流动的情况。

本包使用复式记账法,每笔交易都记录两次(一次记录来源,一次记录目的地)。这确保了账目始终平衡,并且有完整的交易活动审计记录。

如果你的项目管理资金,你应该使用这样的库。你的财务人员会感谢你。

https://github.com/django-oscar/django-oscar-accounts/workflows/Tests/badge.svg Coverage Requirements Status https://img.shields.io/pypi/v/django-oscar-accounts.svg

功能

  • 账户有一个信用额度,默认为0。账户可以设置无信用额度,使其成为系统中的“资金来源”。至少必须设置一个无信用额度的账户,才能使资金在系统中流动。

  • 账户可以有:- 没有分配用户 - 一个“主要”用户 - 这是最常见的情况 - 一组分配的用户

  • 一个用户可以有多个账户

  • 账户可以有一个开始日期和结束日期,以便在有限的时间内使用

  • 账户可以被限制,只能用于支付一系列产品。

  • 账户可以进行分类

屏幕截图

Dashboard account list Create new account Dashboard transfer list Dashboard account detail

安装

使用pip安装

pip install django-oscar-accounts

并将oscar_accounts.apps.AccountsConfigoscar_accounts.dashboard.apps.AccountsDashboardConfig添加到INSTALLED_APPS中。运行manage.py migrate oscar_accounts将创建相应的数据库表。要创建一些初始核心账户和账户类型,请使用manage.py oscar_accounts_init。这些账户的名称可以通过设置来控制(见下文)。

为了使账户可以通过Oscar仪表板访问,您需要将其添加到您的OSCAR_DASHBOARD_NAVIGATION

from oscar.defaults import *

OSCAR_DASHBOARD_NAVIGATION.append(
    {
        'label': 'Accounts',
        'icon': 'fas fa-globe',
        'children': [
            {
                'label': 'Accounts',
                'url_name': 'accounts_dashboard:accounts-list',
            },
            {
                'label': 'Transfers',
                'url_name': 'accounts_dashboard:transfers-list',
            },
            {
                'label': 'Deferred income report',
                'url_name': 'accounts_dashboard:report-deferred-income',
            },
            {
                'label': 'Profit/loss report',
                'url_name': 'accounts_dashboard:report-profit-loss',
            },
        ]
    })

此外,您还需要将url模式添加到您的urls.py

from django.apps import apps

# ...

urlpatterns = [
    ...
    path('dashboard/accounts/', apps.get_app_config('accounts_dashboard').urls),
]

您还应该设置一个cronjob,该cronjob调用

./manage.py close_expired_accounts

以关闭任何过期的账户并将资金转移到“过期”账户。

API

使用管理器创建账户实例

from decimal import Decimal
import datetime

from django.contrib.auth import get_user_model

from oscar_accounts import models

User = get_user_model()

anonymous_account = models.Account.objects.create()

barry = User.objects.get(username="barry")
user_account = models.Account.objects.create(primary_user=barry)

no_credit_limit_account = models.Account.objects.create(credit_limit=None)
credit_limit_account = models.Account.objects.create(credit_limit=Decimal('1000.00'))

today = datetime.date.today()
next_week = today + datetime.timedelta(days=7)
date_limited_account = models.Account.objects.create(
    start_date=today, end_date=next_week)

使用外观进行资金转账

from oscar_accounts import facade

staff_member = User.objects.get(username="staff")
trans = facade.transfer(source=no_credit_limit_account,
                        destination=user_account,
                        amount=Decimal('10.00'),
                        user=staff_member)

反向转账

facade.reverse(trans, user=staff_member,
               description="Just an example")

如果提议的转账无效,将抛出异常。所有异常都是oscar_accounts.exceptions.AccountException的子类。您的客户端代码应查找此类异常并相应地处理它们。

客户端代码应仅使用oscar_accounts.models.Budget类和来自oscar_accounts.facade的两个函数 - 不需要其他任何内容。

错误处理

请注意,转账操作被包含在自己的数据库事务中,以确保只写入完整的转账。当使用Django的事务中间件时,您需要小心。如果您有一个未处理的异常,那么即使其他什么也没有,账户转账仍然会被提交。为了处理这种情况,您需要确保,如果在您的支付后代码中发生异常,则回滚任何转账。

以下是一个示例

from oscar_accounts import facade

def submit(self, order_total):
    # Take payment first
    transfer = facade.transfer(self.get_user_account(),
                               self.get_merchant_account(),
                               order_total)
    # Create order models
    try:
        self.place_order()
    except Exception, e:
        # Something went wrong placing the order.  Roll-back the previous
        # transfer
        facade.reverse(transfer)

在这种情况下,您将创建两个转账,但没有订单。虽然这并不是理想的,但这是处理订单创建过程中发生的异常的最佳方式。

多转账支付

项目通常会允许用户拥有多个账户,并使用多个账户来支付订单。这将涉及多个转账,并在您的应用程序代码中需要一些仔细的处理。

通常,编写围绕账户API的自定义包装器来封装您的业务逻辑和错误处理是有意义的。以下是一个示例:

from decimal import Decimal as D
from oscar_accounts import models, exceptions, facade


def redeem(order_number, user, amount):
    # Get user's non-empty accounts ordered with the first to expire first
    accounts = models.Account.active.filter(
        user=user, balance__gt=0).order_by('end_date')

    # Build up a list of potential transfers that cover the requested amount
    transfers = []
    amount_to_allocate = amount
    for account in accounts:
        to_transfer = min(account.balance, amount_to_allocate)
        transfers.append((account, to_transfer))
        amount_to_allocate -= to_transfer
        if amount_to_allocate == D('0.00'):
            break
    if amount_to_allocate > D('0.00'):
        raise exceptions.InsufficientFunds()

    # Execute transfers to some 'Sales' account
    destination = models.Account.objects.get(name="Sales")
    completed_transfers = []
    try:
        for account, amount in transfers:
            transfer = facade.transfer(
                account, destination, amount, user=user,
                description="Order %s" % order_number)
            completed_transfers.append(transfer)
    except exceptions.AccountException, transfer_exc:
        # Something went wrong with one of the transfers (possibly a race condition).
        # We try and roll back all completed ones to get us back to a clean state.
        try:
            for transfer in completed_transfers:
                facade.reverse(transfer)
        except Exception, reverse_exc:
            # Uh oh: No man's land.  We could be left with a partial
            # redemption. This will require an admin to intervene.  Make
            # sure your logger mails admins on error.
            logger.error("Order %s, transfers failed (%s) and reverse failed (%s)",
                         order_number, transfer_exc, reverse_exc)
            logger.exception(reverse_exc)

        # Raise an exception so that your client code can inform the user appropriately.
        raise RedemptionFailed()
    else:
        # All transfers completed ok
        return completed_transfers

如您所见,对无法执行所有转账的情况进行了仔细处理。

如果您使用Oscar,请确保为每次转账创建一个OrderSource实例(而不是将它们全部聚合到一个实例中)。这将提供更好的审计信息。以下是一些示例代码:

try:
    transfers = api.redeem(order_number, user, total_incl_tax)
except Exception:
    # Inform user of failed payment
else:
    for transfer in transfers:
        source_type, __ = SourceType.objects.get_or_create(name="Accounts")
        source = Source(
            source_type=source_type,
            amount_allocated=transfer.amount,
            amount_debited=transfer.amount,
            reference=transfer.reference)
        self.add_payment_source(source)

核心账户和账户类型

post-syncdb信号将创建账户类型和账户的通用结构。一些名称可以通过设置进行控制,如括号所示。

  • 资产

    • 销售

      • 赎回(ACCOUNTS_REDEMPTIONS_NAME)-当使用账户支付某物时,资金将转入此处。

      • 过期(ACCOUNTS_LAPSED_NAME)-当账户到期时,资金将转入此处。这是通过“close_expired_accounts”管理命令完成的。此账户的名称可以通过ACCOUNTS_LAPSED_NAME进行设置。

    • 现金

      • “银行”(ACCOUNTS_BANK_NAME)-由客户支付创建新账户的源账户(例如,礼品卡)。此账户不会有信用额度,并且通常具有负余额,因为只有资金被转出。

    • 未支付 - 这包含作为其他账户来源但未由客户支付的账户。例如,您可能允许管理员在仪表板中创建新账户。此类账户将是初始转账的源账户。

  • 负债

    • 递延收入 - 这包含客户账户/礼品卡。您可能希望在此类型内创建额外的账户类型以对账户进行分类。

示例交易

考虑以下账户和账户类型:

  • 资产
    • 销售
      • 赎回

      • 过期

    • 现金
      • 银行

    • 未支付
      • 商家资助

  • 负债
    • 递延收入

请注意,所有账户的初始余额都是0,所有余额的总和始终为0。

一位客户购买了一张50英镑的礼品卡

  • 创建了一个类型为“递延收入”的新账户,并设置了结束日期 - 50英镑从银行转账到这个新账户

一位客户使用他们的50英镑礼品卡支付了30英镑的订单

  • 30英镑从礼品卡账户转账到赎回账户

客户的礼品卡过期,卡上还剩20英镑

  • 20英镑从礼品卡账户转账到过期账户

客户打电话投诉,一名员工创建了一张20英镑的新礼品卡

  • 创建了一个类型为“递延收入”的新账户 - 20英镑从“商家资助”账户转账到这个新账户

设置

有一些设置可以控制命名和初始未支付和递延收入账户类型

  • ACCOUNTS_MIN_LOAD_VALUE 创建账户(或充值)的最小值

  • ACCOUNTS_MAX_INITIAL_VALUE 可以转账到账户的最大值。

  • OSCAR_ACCOUNTS_DASHBOARD_ITEMS_PER_PAGE 每页仪表板中显示的项目数量(默认=20)。

贡献

分支存储库,设置虚拟环境并运行

make install

使用以下命令运行测试

pytest

项目详细信息


下载文件

下载适用于您的平台的文件。如果您不确定选择哪个,请了解有关安装包的更多信息。

源代码分发

django-oscar-accounts-3.0.1.tar.gz (821.0 kB 查看哈希值)

上传时间 源代码

构建分发

django_oscar_accounts-3.0.1-py3-none-any.whl (51.3 kB 查看哈希值)

上传时间 Python 3

由支持

AWSAWS云计算和安全赞助商DatadogDatadog监控FastlyFastlyCDNGoogleGoogle下载分析MicrosoftMicrosoftPSF赞助商PingdomPingdom监控SentrySentry错误日志StatusPageStatusPage状态页面