From 02feb824bd56682e4fb52b21034f5492b914bc82 Mon Sep 17 00:00:00 2001 From: Marc Date: Wed, 23 Jul 2014 16:24:56 +0000 Subject: [PATCH] Added payments and bills scafolding --- TODO.md | 3 + orchestra/admin/menu.py | 4 +- .../apps/{invoices => bills}/__init__.py | 0 orchestra/apps/bills/admin.py | 50 + orchestra/apps/bills/api.py | 15 + orchestra/apps/bills/filters.py | 39 + orchestra/apps/bills/models.py | 145 +- orchestra/apps/bills/serializers.py | 15 + orchestra/apps/bills/settings.py | 8 + orchestra/apps/miscellaneous/admin.py | 2 +- orchestra/apps/payments/__init__.py | 0 orchestra/apps/payments/admin.py | 7 + orchestra/apps/payments/api.py | 21 + orchestra/apps/payments/methods.py | 15 + orchestra/apps/payments/models.py | 44 + orchestra/apps/payments/serializers.py | 14 + orchestra/apps/payments/settings.py | 4 + orchestra/bin/orchestra-admin | 3 +- orchestra/conf/base_settings.py | 14 +- orchestra/static/orchestra/icons/gauge.png | Bin 2780 -> 3887 bytes orchestra/static/orchestra/icons/gauge.svg | 1961 +++++++++-------- orchestra/static/orchestra/icons/order.svg | 380 ++-- .../static/orchestra/icons/transaction.png | Bin 4100 -> 3320 bytes .../static/orchestra/icons/transaction.svg | 730 ++---- 24 files changed, 1876 insertions(+), 1598 deletions(-) rename orchestra/apps/{invoices => bills}/__init__.py (100%) create mode 100644 orchestra/apps/bills/admin.py create mode 100644 orchestra/apps/bills/api.py create mode 100644 orchestra/apps/bills/filters.py create mode 100644 orchestra/apps/bills/serializers.py create mode 100644 orchestra/apps/bills/settings.py create mode 100644 orchestra/apps/payments/__init__.py create mode 100644 orchestra/apps/payments/admin.py create mode 100644 orchestra/apps/payments/api.py create mode 100644 orchestra/apps/payments/methods.py create mode 100644 orchestra/apps/payments/models.py create mode 100644 orchestra/apps/payments/serializers.py create mode 100644 orchestra/apps/payments/settings.py diff --git a/TODO.md b/TODO.md index 4f1eb6f8..a32ec73c 100644 --- a/TODO.md +++ b/TODO.md @@ -62,3 +62,6 @@ Remember that, as always with QuerySets, any subsequent chained methods which im * DOCUMENT: orchestration.middleware: we need to know when an operation starts and ends in order to perform bulk server updates and also to wait for related objects to be saved (base object is saved first and then related) orders.signales: we perform changes right away because data model state can change under monitoring and other periodik task, and we should keep orders consistency under any situation. dependency collector with max_recursion that matches the number of dots on service.match and service.metric + + +* Be consistent with dates: name_on, created ? diff --git a/orchestra/admin/menu.py b/orchestra/admin/menu.py index 04b7a7a5..3fb6bffc 100644 --- a/orchestra/admin/menu.py +++ b/orchestra/admin/menu.py @@ -58,8 +58,6 @@ def get_accounts(): if isinstalled('orchestra.apps.orders'): url = reverse('admin:orders_order_changelist') accounts.append(items.MenuItem(_("Orders"), url)) - url = reverse('admin:orders_service_changelist') - accounts.append(items.MenuItem(_("Services"), url)) return accounts @@ -80,6 +78,8 @@ def get_administration_models(): administration_models.append('orchestra.apps.resources.*') if isinstalled('orchestra.apps.miscellaneous'): administration_models.append('orchestra.apps.miscellaneous.models.MiscService') + if isinstalled('orchestra.apps.orders'): + administration_models.append('orchestra.apps.orders.models.Service') return administration_models diff --git a/orchestra/apps/invoices/__init__.py b/orchestra/apps/bills/__init__.py similarity index 100% rename from orchestra/apps/invoices/__init__.py rename to orchestra/apps/bills/__init__.py diff --git a/orchestra/apps/bills/admin.py b/orchestra/apps/bills/admin.py new file mode 100644 index 00000000..daba9b73 --- /dev/null +++ b/orchestra/apps/bills/admin.py @@ -0,0 +1,50 @@ +from django.contrib import admin +from django.core.urlresolvers import reverse +from django.utils.translation import ugettext_lazy as _ + +from orchestra.admin.utils import admin_link, admin_date +from orchestra.apps.accounts.admin import AccountAdminMixin + +from .filters import BillTypeListFilter +from .models import (Bill, Invoice, AmendmentInvoice, Fee, AmendmentFee, Budget, + BillLine, BudgetLine) + + +class BillLineInline(admin.TabularInline): + model = BillLine + +class BudgetLineInline(admin.TabularInline): + model = Budget + + +class BillAdmin(AccountAdminMixin, admin.ModelAdmin): + list_display = ( + 'ident', 'status', 'bill_type_link', 'account_link', 'created_on_display' + ) + list_filter = (BillTypeListFilter, 'status',) + readonly_fields = ('ident',) + inlines = [BillLineInline] + + account_link = admin_link('account') + created_on_display = admin_date('created_on') + + def bill_type_link(self, bill): + bill_type = bill.bill_type.lower() + url = reverse('admin:bills_%s_changelist' % bill_type) + return '%s' % (url, bill.get_bill_type_display()) + bill_type_link.allow_tags = True + bill_type_link.short_description = _("type") + bill_type_link.admin_order_field = 'bill_type' + + def get_inline_instances(self, request, obj=None): + if self.model is Budget: + self.inlines = [BudgetLineInline] + return super(BillAdmin, self).get_inline_instances(request, obj=obj) + + +admin.site.register(Bill, BillAdmin) +admin.site.register(Invoice, BillAdmin) +admin.site.register(AmendmentInvoice, BillAdmin) +admin.site.register(Fee, BillAdmin) +admin.site.register(AmendmentFee, BillAdmin) +admin.site.register(Budget, BillAdmin) diff --git a/orchestra/apps/bills/api.py b/orchestra/apps/bills/api.py new file mode 100644 index 00000000..b96346fc --- /dev/null +++ b/orchestra/apps/bills/api.py @@ -0,0 +1,15 @@ +from rest_framework import viewsets + +from orchestra.api import router +from orchestra.apps.accounts.api import AccountApiMixin + +from .models import Bill +from .serializers import BillSerializer + + +class BillViewSet(AccountApiMixin, viewsets.ModelViewSet): + model = Bill + serializer_class = BillSerializer + + +router.register(r'bills', BillViewSet) diff --git a/orchestra/apps/bills/filters.py b/orchestra/apps/bills/filters.py new file mode 100644 index 00000000..4d52cfa8 --- /dev/null +++ b/orchestra/apps/bills/filters.py @@ -0,0 +1,39 @@ +from django.contrib.admin import SimpleListFilter +from django.core.urlresolvers import reverse +from django.utils.translation import ugettext_lazy as _ + + +class BillTypeListFilter(SimpleListFilter): + """ Filter tickets by created_by according to request.user """ + title = 'Type' + parameter_name = '' + + def __init__(self, request, *args, **kwargs): + super(BillTypeListFilter, self).__init__(request, *args, **kwargs) + self.request = request + + def lookups(self, request, model_admin): + return ( + ('bill', _("All")), + ('invoice', _("Invoice")), + ('amendmentinvoice', _("Amendment invoice")), + ('fee', _("Fee")), + ('fee', _("Amendment fee")), + ('budget', _("Budget")), + ) + + + def queryset(self, request, queryset): + return queryset + + def value(self): + return self.request.path.split('/')[-2] + + def choices(self, cl): + for lookup, title in self.lookup_choices: + yield { + 'selected': self.value() == lookup, + 'query_string': reverse('admin:bills_%s_changelist' % lookup), + 'display': title, + } + diff --git a/orchestra/apps/bills/models.py b/orchestra/apps/bills/models.py index 453565bd..7c96c558 100644 --- a/orchestra/apps/bills/models.py +++ b/orchestra/apps/bills/models.py @@ -1,13 +1,148 @@ from django.db import models +from django.utils import timezone +from django.utils.translation import ugettext_lazy as _ + +from . import settings + + +class BillManager(models.Manager): + def get_queryset(self): + queryset = super(BillManager, self).get_queryset() + if self.model != Bill: + bill_type = self.model.get_type() + queryset = queryset.filter(bill_type=bill_type) + return queryset class Bill(models.Model): + OPEN = 'OPEN' + CLOSED = 'CLOSED' + SEND = 'SEND' + RETURNED = 'RETURNED' + PAID = 'PAID' + BAD_DEBT = 'BAD_DEBT' + STATUSES = ( + (OPEN, _("Open")), + (CLOSED, _("Closed")), + (SEND, _("Sent")), + (RETURNED, _("Returned")), + (PAID, _("Paid")), + (BAD_DEBT, _("Bad debt")), + ) + + TYPES = ( + ('INVOICE', _("Invoice")), + ('AMENDMENTINVOICE', _("Amendment invoice")), + ('FEE', _("Fee")), + ('AMENDMENTFEE', _("Amendment Fee")), + ('BUDGET', _("Budget")), + ) + + ident = models.CharField(_("identifier"), max_length=16, unique=True, + blank=True) + account = models.ForeignKey('accounts.Account', verbose_name=_("account"), + related_name='%(class)s') + bill_type = models.CharField(_("type"), max_length=16, choices=TYPES) + status = models.CharField(_("status"), max_length=16, choices=STATUSES, + default=OPEN) + created_on = models.DateTimeField(_("created on"), auto_now_add=True) + due_on = models.DateTimeField(_("due on"), null=True, blank=True) + last_modified_on = models.DateTimeField(_("last modified on"), auto_now=True) + #base = models.DecimalField(max_digits=12, decimal_places=2) + #tax = models.DecimalField(max_digits=12, decimal_places=2) + comments = models.TextField(_("comments"), blank=True) + # TODO rename to HTML-agnostic term like.. RAW ? + html = models.TextField(_("HTML"), blank=True) + + objects = BillManager() + + def __unicode__(self): + return self.ident + + @classmethod + def get_type(cls): + return cls.__name__.upper() + + def set_ident(self): + cls = type(self) + bill_type = self.bill_type or cls.get_type() + if bill_type == 'BILL': + raise TypeError("get_new_ident() can not be used on a Bill class") + # Bill number resets every natural year + year = timezone.now().strftime("%Y") + bills = cls.objects.filter(created_on__year=year) + number_length = settings.BILLS_IDENT_NUMBER_LENGTH + prefix = getattr(settings, 'BILLS_%s_IDENT_PREFIX' % bill_type) + if self.status == self.OPEN: + prefix = 'O{}'.format(prefix) + bills = bills.filter(status=self.OPEN) + num_bills = bills.order_by('-ident').first() or 0 + if num_bills is not 0: + num_bills = int(num_bills.ident[-number_length:]) + else: + bills = bills.exclude(status=self.OPEN) + num_bills = bills.count() + zeros = (number_length - len(str(num_bills))) * '0' + number = zeros + str(num_bills + 1) + self.ident = '{prefix}{year}{number}'.format( + prefix=prefix, year=year, number=number) + + def save(self, *args, **kwargs): + if not self.bill_type: + self.bill_type = type(self).get_type() + if not self.ident or (self.ident.startswith('O') and self.status != self.OPEN): + self.set_ident() + super(Bill, self).save(*args, **kwargs) + + +class Invoice(Bill): + class Meta: + proxy = True + + +class AmendmentInvoice(Bill): + class Meta: + proxy = True + + +class Fee(Bill): + class Meta: + proxy = True + + +class AmendmentFee(Bill): + class Meta: + proxy = True + + +class Budget(Bill): + class Meta: + proxy = True + + +class BaseBillLine(models.Model): + bill = models.ForeignKey(Bill, verbose_name=_("bill"), + related_name='%(class)ss') + description = models.CharField(max_length=256) + initial_date = models.DateTimeField() + final_date = models.DateTimeField() + price = models.DecimalField(max_digits=12, decimal_places=2) + amount = models.IntegerField() + tax = models.DecimalField(max_digits=12, decimal_places=2) + + class Meta: + abstract = True + + +class BudgetLine(BaseBillLine): pass -class Invoice(models.Model): - pass +class BillLine(BaseBillLine): + order_id = models.PositiveIntegerField(blank=True) + order_last_bill_date = models.DateTimeField(null=True) + order_billed_until = models.DateTimeField(null=True) + auto = models.BooleanField(default=False) + amended_line = models.ForeignKey('self', verbose_name=_("amended line"), + related_name='amendment_lines', null=True, blank=True) - -class Fee(models.Model): - pass diff --git a/orchestra/apps/bills/serializers.py b/orchestra/apps/bills/serializers.py new file mode 100644 index 00000000..6233e7fa --- /dev/null +++ b/orchestra/apps/bills/serializers.py @@ -0,0 +1,15 @@ +from rest_framework import serializers + +from .models import Bill, BillLine + + +class BillLineSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = BillLine + + +class BillSerializer(serializers.HyperlinkedModelSerializer): + lines = BillLineSerializer(source='billlines') + + class Meta: + model = Bill diff --git a/orchestra/apps/bills/settings.py b/orchestra/apps/bills/settings.py new file mode 100644 index 00000000..45f58ffb --- /dev/null +++ b/orchestra/apps/bills/settings.py @@ -0,0 +1,8 @@ +from django.conf import settings + +BILLS_IDENT_NUMBER_LENGTH = getattr(settings, 'BILLS_IDENT_NUMBER_LENGTH', 4) +BILLS_INVOICE_IDENT_PREFIX = getattr(settings, 'BILLS_INVOICE_IDENT_PREFIX', 'I') +BILLS_AMENDMENT_INVOICE_IDENT_PREFIX = getattr(settings, 'BILLS_AMENDMENT_INVOICE_IDENT_PREFIX', 'A') +BILLS_FEE_IDENT_PREFIX = getattr(settings, 'BILLS_FEE_IDENT_PREFIX', 'F') +BILLS_AMENDMENT_FEE_IDENT_PREFIX = getattr(settings, 'BILLS_AMENDMENT_FEE_IDENT_PREFIX', 'B') +BILLS_BUDGET_IDENT_PREFIX = getattr(settings, 'BILLS_BUDGET_IDENT_PREFIX', 'Q') diff --git a/orchestra/apps/miscellaneous/admin.py b/orchestra/apps/miscellaneous/admin.py index 1b506b30..0221a611 100644 --- a/orchestra/apps/miscellaneous/admin.py +++ b/orchestra/apps/miscellaneous/admin.py @@ -32,7 +32,7 @@ class MiscellaneousAdmin(AccountAdminMixin, admin.ModelAdmin): def get_fields(self, request, obj=None): if obj is None: return ('service', 'account', 'description', 'amount', 'is_active') - if not obj.service.has_amount: + elif not obj.service.has_amount: return ('service', 'account_link', 'description', 'is_active') return ('service', 'account_link', 'description', 'amount', 'is_active') diff --git a/orchestra/apps/payments/__init__.py b/orchestra/apps/payments/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/orchestra/apps/payments/admin.py b/orchestra/apps/payments/admin.py new file mode 100644 index 00000000..4c150b72 --- /dev/null +++ b/orchestra/apps/payments/admin.py @@ -0,0 +1,7 @@ +from django.contrib import admin + +from .models import PaymentSource, Transaction + + +admin.site.register(PaymentSource) +admin.site.register(Transaction) diff --git a/orchestra/apps/payments/api.py b/orchestra/apps/payments/api.py new file mode 100644 index 00000000..16246b3e --- /dev/null +++ b/orchestra/apps/payments/api.py @@ -0,0 +1,21 @@ +from rest_framework import viewsets + +from orchestra.api import router +from orchestra.apps.accounts.api import AccountApiMixin + +from .models import PaymentSource, Transaction +from .serializers import PaymentSourceSerializer, TransactionSerializer + + +class PaymentSourceViewSet(AccountApiMixin, viewsets.ModelViewSet): + model = PaymentSource + serializer_class = PaymentSourceSerializer + + +class TransactionViewSet(viewsets.ModelViewSet): + model = Transaction + serializer_class = TransactionSerializer + + +router.register(r'payment-sources', PaymentSourceViewSet) +router.register(r'transactions', TransactionViewSet) diff --git a/orchestra/apps/payments/methods.py b/orchestra/apps/payments/methods.py new file mode 100644 index 00000000..ff132bc4 --- /dev/null +++ b/orchestra/apps/payments/methods.py @@ -0,0 +1,15 @@ +from django.utils.translation import ugettext_lazy as _ + +from orchestra.utils import plugins + + +class PaymentMethod(plugins.Plugin): + __metaclass__ = plugins.PluginMount + + +class BankTransfer(PaymentMethod): + verbose_name = _("Bank transfer") + + +class CreditCard(PaymentMethod): + verbose_name = _("Credit card") diff --git a/orchestra/apps/payments/models.py b/orchestra/apps/payments/models.py new file mode 100644 index 00000000..a2f7a79c --- /dev/null +++ b/orchestra/apps/payments/models.py @@ -0,0 +1,44 @@ +from django.db import models +from django.utils.translation import ugettext_lazy as _ +from jsonfield import JSONField + +from . import settings +from .methods import PaymentMethod + + +class PaymentSource(models.Model): + account = models.ForeignKey('accounts.Account', verbose_name=_("account"), + related_name='payment_sources') + method = models.CharField(_("method"), max_length=32, + choices=PaymentMethod.get_plugin_choices()) + data = JSONField(_("data")) + + +class Transaction(models.Model): + WAITTING_PROCESSING = 'WAITTING_PROCESSING' + WAITTING_CONFIRMATION = 'WAITTING_CONFIRMATION' + CONFIRMED = 'CONFIRMED' + REJECTED = 'REJECTED' + LOCKED = 'LOCKED' + DISCARTED = 'DISCARTED' + STATES = ( + (WAITTING_PROCESSING, _("Waitting for processing")), + (WAITTING_CONFIRMATION, _("Waitting for confirmation")), + (CONFIRMED, _("Confirmed")), + (REJECTED, _("Rejected")), + (LOCKED, _("Locked")), + (DISCARTED, _("Discarted")), + ) + + bill = models.ForeignKey('bills.bill', verbose_name=_("bill"), + related_name='transactions') + method = models.CharField(_("payment method"), max_length=32, + choices=PaymentMethod.get_plugin_choices()) + state = models.CharField(_("state"), max_length=32, choices=STATES, + default=WAITTING_PROCESSING) + data = JSONField(_("data")) + amount = models.DecimalField(_("amount"), max_digits=12, decimal_places=2) + currency = models.CharField(max_length=10, default=settings.PAYMENT_CURRENCY) + created_on = models.DateTimeField(auto_now_add=True) + modified_on = models.DateTimeField(auto_now=True) + related = models.ForeignKey('self', null=True, blank=True) diff --git a/orchestra/apps/payments/serializers.py b/orchestra/apps/payments/serializers.py new file mode 100644 index 00000000..ed164f89 --- /dev/null +++ b/orchestra/apps/payments/serializers.py @@ -0,0 +1,14 @@ +from rest_framework import serializers + +from .models import PaymentSource, PaymentSource + + +class PaymentSourceSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = PaymentSource + + +class TransactionSerializer(serializers.HyperlinkedModelSerializer): + + class Meta: + model = PaymentSource diff --git a/orchestra/apps/payments/settings.py b/orchestra/apps/payments/settings.py new file mode 100644 index 00000000..51c0f344 --- /dev/null +++ b/orchestra/apps/payments/settings.py @@ -0,0 +1,4 @@ +from django.conf import settings + + +PAYMENT_CURRENCY = getattr(settings, 'PAYMENT_CURRENCY', 'Eur') diff --git a/orchestra/bin/orchestra-admin b/orchestra/bin/orchestra-admin index 42638df1..f44d7456 100755 --- a/orchestra/bin/orchestra-admin +++ b/orchestra/bin/orchestra-admin @@ -143,7 +143,8 @@ function install_requirements () { paramiko==1.12.1 \ Pygments==1.6 \ django-filter==0.7 \ - passlib==1.6.2" + passlib==1.6.2 \ + jsonfield==0.9.22" if $testing; then APT="${APT} \ diff --git a/orchestra/conf/base_settings.py b/orchestra/conf/base_settings.py index 941f5680..2672a067 100644 --- a/orchestra/conf/base_settings.py +++ b/orchestra/conf/base_settings.py @@ -79,6 +79,8 @@ INSTALLED_APPS = ( 'orchestra.apps.prices', 'orchestra.apps.orders', 'orchestra.apps.miscellaneous', + 'orchestra.apps.bills', + 'orchestra.apps.payments', # Third-party apps 'django_extensions', @@ -140,8 +142,9 @@ FLUENT_DASHBOARD_APP_GROUPS = ( 'orchestra.apps.contacts.models.Contact', 'orchestra.apps.users.models.User', 'orchestra.apps.orders.models.Order', - 'orchestra.apps.orders.models.Service', 'orchestra.apps.prices.models.Pack', + 'orchestra.apps.bills.models.Bill', + 'orchestra.apps.payments.models.Transaction', ), 'collapsible': True, }), @@ -154,6 +157,7 @@ FLUENT_DASHBOARD_APP_GROUPS = ( 'orchestra.apps.issues.models.Ticket', 'orchestra.apps.resources.models.Resource', 'orchestra.apps.resources.models.Monitor', + 'orchestra.apps.orders.models.Service', ), 'collapsible': True, }), @@ -172,20 +176,22 @@ FLUENT_DASHBOARD_APP_ICONS = { 'databases/database': 'database.png', 'databases/databaseuser': 'postgresql.png', 'vps/vps': 'TuxBox.png', - 'miscellaneous/miscellaneous': 'Misc-Misc-Box-icon.png', + 'miscellaneous/miscellaneous': 'applications-other.png', # Accounts 'accounts/account': 'Face-monkey.png', 'contacts/contact': 'contact.png', 'orders/order': 'basket.png', 'orders/service': 'price.png', - 'prices/pack': 'Dialog-accept.png', + 'prices/pack': 'Pack.png', + 'bills/bill': 'invoice.png', + 'payments/transaction': 'transaction.png', # Administration 'users/user': 'Mr-potato.png', 'djcelery/taskstate': 'taskstate.png', 'orchestration/server': 'vps.png', 'orchestration/route': 'hal.png', 'orchestration/backendlog': 'scriptlog.png', - 'issues/ticket': "Ticket_star.png", + 'issues/ticket': 'Ticket_star.png', 'resources/resource': "gauge.png", 'resources/monitor': "Utilities-system-monitor.png", } diff --git a/orchestra/static/orchestra/icons/gauge.png b/orchestra/static/orchestra/icons/gauge.png index d5d2e57eb227d169ef68004360f704bd5c8a1db5..686ebd044625955f6edf20d375d141e2aa2ec4b2 100644 GIT binary patch delta 3814 zcmV6|WwUZGR4|NklfHNo)&14?Q+2EAck4bOrR4v!8GmlJ!X4EHcLJx=)IV*;%$f>HLKC0<@vdLt0_lSy? zaj~&Ri^XE}c)Ys9;qbYfPQBi4SKaO=RVd;+U6UUPseZm_(V~j~6@XQ%mXA{n>T5=$ zX~587!$^paH-Fl~BQTlFD2jq#^P}q;s-hxo2Ry}wo3Z_6BaF6Qg~AVmMvRm2o5p5ZZI03d}Q_` zrm#>eb$|9+ii!%^zGEjYhl`Ai4ARol=+&ziicmWAQ**h76DLn_@R1+1yuAG$U5XhmE`0Hun*#93C!eTg7tU-A4h|kUW5z6t*=(ZtOc7hY z+CpVz71O3pBP~4*fk06dgb;v0h#!4O2`zO=bbnn3prWFJPe1zvhr`LF$@h_(I*{ld zF%%V_X4|%Hesuj;ixw?@|7Qc_yt?v@kg(9dn>BN`#b7XS!qrvc}Cw}=WlfhtM_uids`FbmJ9-G5}{sXQqtO(AZFQdG?g6f*fRM*tta=B0y zg{Y_=L`Ovv6&1;#)Ktu7^HsOj+v{1ob}h-hdoktyDa6MlQe0Xnb8~YWJowWV|9bI- zZUd}*b>;0U9R8iPSa*L5^qV}IU3KHc;=wY9ZB zIv0YD{F}^XCXO4+gZEDkfGCQB$K&D6HE)uX+>`O+#}glu$gVxx{U=Wq6gD}U)0Qq> zs<-#m@knbmqY!^eNg1FdCHA89Yzd!!{25O@`4rL7(U?pogit6hEoIruD_Q&YJAYS+ z2RalF;PrU;;_EFe%v!?U@4iD(6jaqfXlN+&=glYo$T9Md=F{M?GxE;63?`GYZ%~lw zv5tIo3Xq+>e4>P*BQoz$-OWvGc>jGSO`1&a-n}sxjOe;X?#>;&w0sq3&YbP49Xd2! z*D1SLPR<`z^Ky0$e!mYX!D_Yg^nd*MeD%fGoWF1uMNt`l?*xl5h*wsxUTtpgr!xSd z8r45$W@Z{gL#%weZx2R;k&$=a34qV%<=~-1yz?I$&~>d_!X?n?a8gxW!_eD?u;IP8 zxp3hkuf4G*Fu0R?CNcW%(d^#!Ej6`O+>(3?Q9YupZcpRWHw2KK{qjhl&wrqSgGGHq zE!(&6WXALvf&6o2Wvp4ZzGZIghUvOaU2Ppsr<2j6MzZ9^g+$nF{Na@q96xcAPd9xY zcs_^S26$|uZKUb zeiN_P+b!`@N^0xs@p!!q8-IE`i(g!b!O#*6hlGT%$)#f8N* zIvX$g1@>MzX?b8RPb;+JN?IGU(@MA8PSI{#t zfw0gpBBP=>f9^c7(SPv_zV%j1>De>0f#re5ZZAMceW2NFZVn3x<#gd`dL|}fX%$jf zTugO!O{e085Ip;j^ZCUi4+q3U%N(x=!Ib+Z^46Nw2qF0A1utN?H}LD{o(Yr-54SQX zHHB}s0q^EPH^eoNI%~-?2(KYR=)w2TtB`zu|N~o%Vnt#hx#Kgn~^7ifTbVUbX z`qT%xEhGIJDA6V;>Jb@9Y)lO9=4KwAK8?`OkgLjP4j;zW+#P}6u`#hUG&bPZ{Fu!a zq?DFr%a(<93IKXsNLZ*rB57!7BqA~*kW*At+NpS>(a41HwMNJ5@1ev5Rak*T? z#|Fw$XRq&6SVnrHKvjrcVmilSh#SUetgb39PgA%B3>b+-^gN-2d9A`mr7B==9c zk48r$jSUVO9E~^{8?iS!aJgJ0^-S!TtX2QUk3V7aU%y0ERerNL3$w*cWmOeVJT(uW z<|oo-!)CKFdEx|Yws0aNBY~E+LlG*X)se32ij=yi^B#~~(tJLzE_HKgSSWV8y;X-` z>iv^D)PJb!I+}Lvid<)}XV0E}c)VUVZ2Sm-NfXDDk)DRG>umjEGiI}?WAIjMXlNkJ z8j8_q!teLv@pw!~Ltqo2gKZU2+1%{a6-B`sW~Jidr7kOg)>%fmdN{3H|1RGhI7mgs zC8U%HA&9Wqc;@MOfqSke{tAHA8iv7OpxNU_2!A1M5fK4V*9>B*%F2u77f^&kbWAiA z72Vz_pro{vef#s!HJ!}i!?0SdNGZ8%@0Pm!xJv*4zhB!}R8-g;WC>>Az!di9 zeb+63v19JW-Rx%1-hJFZZ6>e1u?B$YQ-2@mmY=)>d8DSM5*8YctI0`qO|?bA`^9wu zRH1J!EiEzWx<*FE5IoHu3Qpg6FZ76r{fhtug~ z&z?PG-Z30&SU6|So{@^89A2>^8uH1xu?n$9Lz}d5BS-C2k+lSr8h|J+c+M?L~?Ji$+P0ewa z(=)fT`)+7!F*|4ZEJY|AX3m^xjE#7r{bb>Ozn@*Zcagh2 zmub_dlAbn%2wNn_j~~_d?tk4|?epqsSy@?~zD3>e?)0jhm!AXW)dwG#YUq`eOj-Fk zcI?=};UA80*QmRgIALPo&Fw}^O3C*>e9xxMn@C8EXX3<(4Ct4Fs;KPQyT^CBprBUQ zd}9|aTGHkG-L3Bf+1bk{3RQeIZrr_=L8*fkSCf+q73bNpeJ96{9e<}!pT1xB}PMjbvE|zikjwNM4Dv`D*?2UH5{PM5OjSUUQeO@`G ztCs&s001j~zhZz9<(rt8nE26mkG8}_$I;|=QCVHd>C>kuDk`D;;zb-zCq{!2YnTlD8c+;qncUj_M;?Q&rpU;cW=f&&u;BqzLa5@MH3B?*_#b_{~8Vm$k zf{{|v&`?iNagjWH_^?+>dD5rp&t)yl>Q>xx!vNySSBsXU0DsEt*|V)_8H4`SU@*FDg9+)Vv^Bjvk@E=jKyq0O7Lrbd_Er@uZL#0n~F>2oH%*B$u&J=1c+2L^n=f4v-3Fk2(g3|9tyclGw(=Fx?T?)ST?1z_ znUukU2S+3)C&$`)*rIix&s|efU0qO6Ty^f;xuz}zYn}YQ>L{fQd?yG1v}^IU$Ii|> zgKQ5(tJt=JwGPhP9R&<#NlRZGQ?`Nkl4586~}*f7SDJVJG0o6@kU}Ra^j#QDkT&U zgfz6QZJ}rid(Zj*&pqed^M9^JNGbV$Zl3cYw7tFE zr6@|BrfD82rEs}iMmQYSQmK?K&#}eCxsaDgB-l|N{jrd$vLLI@Fy#TLio@yA!L zTzPXO62UahDWI#@wZFB`bX{k3bTpq%ryn#7<7rh@%jbFL>$NF`FY1&I(uXikwNT_{% zecc|9=Q3T_pVM`{=<#@h9*?KR>-FB#*w}bOLqkKC%jKGMJHs$2l}hCE`EM5rg-4{6 z+F272LVt*8G`fDxnl-;jBogte)pcE`udgrN+uOUnP$)dMbm`L6-o1NI5f%!Cez1J` z@-2rCAHGCUlzlaF>wG@n_0eebE}zf0-sAC15;0AaTrRh}TrO|WG%Y)C0zwGU+S+>O z6<1vGn`ksTX{A&u#ryBS|K#ZC=$5guv9ap-#(&1fuxXmByZ{*wZf>3gzkmP!{R0C7w`MY#et?FCh6RyG+$jN z1I1$TQ<|n71Kfc?;I2R*@RZx_W@2Ij!!Rh9%eR}R`OFy!2q8pgXXia@)~xv%fPY~a z^!N7{4<0h9eqQaWs>(v4@aamWvIB4d z-vqwvfHndwmr|Y*53X894Gj(L?d|P9tOCnsv+UcqZ(}BtIby%RY}vA9&xS&wrs+1N z(`{T%63#v`F+o0`7nMroS>O_T4}W+Rc+mkZ0sL49A!Z~XgrK9N<6G_R?S8{B&@_!> z$Bu1}Mx#3cVzF39cX#)%gTde_fU8~8v?(NJa;Z|OL?)AAczBrc@$sBt7@q}x1^iA3 z(I%x-fqMa`i`)q;nvp<5LqjAIiQHYa{?O16$BrG_+}GD<#$qvdb93`ABY%;|(prnx z0(Fvcj11FL~D@CI-d@O9ui;D2U&-1ln;Y!pHSrVv=WcCF~@>bj{4FqKN7D9S@pN*RyG z-L0*yo84~r6jyP&PbQN&G%zsmkyI+Ru2?KSs;X*VKA%Tb)#Ig7=~2@(*8!IShk!m{ z3lIVRVY|-@z^lNMz!o8dA4mbebr6UES56`D)?04{HBI|ul|Zpre19_(3Z(!vO}i)* z3SBZ?&^ijS!=t04*OklVH>!o0+wJ~yE|=>t3}Xj?l+ut=z6sm_2&&9 zRaJ+aTFT{eSxPw|rGKm~kxu|$13nD+fzJSSLWr;sA`GN~gTMxR{JVg&uv;R8@Pgx$ z(lpJRfJsn=LSZ*RGMRJ-0)eZXxxO|Xj*X2yrK;*Mvu~FJKew;jY=E`R2N*&K7f=HJ z3fyeJ`zR0sa+3st!C<#DFPorUq3zD z4;Nsf<~|G#7YYW0KBr&1T&^jF(;NW{EH`jtxxj6}AMMw>flYG&4^WGz85}`un&v5+ z&)GB~LC;|8$AAsMARZ;%ezRv;&<|#K^ zb3YCaR;5zusIFUXW*7!Sh>Njy%UL(Oe))ubyAAlNlz-9?LcETZ2A2Uz;K&^13ZT{T zsD$O7r_@3(IE$`8An>udD5>hgN{?XGH`O3@ZgBH)%^)A_0RTI(S74GrrBWFj8yl0= zAoh4XSK1bxOVhD(JXodhSA-DjfF1U;<5(5uT-(Z;`vEDXm1!KClj!K^*uG@R64}+& zCEMECq<_!n>#V+?Z3lXQeyl7}+i!R3oTWU0RWmu|Tv~lUxdKv3nayT*YnoP#0sMad zL$zN(XKGT)!@xCI6=$^-fzJWIpT}8j3vi3$K8IB&O)8x1KA*2Kkw}y}Iyz)qTbqnT zA`)0M*Hy4VdayQ|R-k^KG6Ys>?X><+PZ?7?DSr})Jl@jMB9qCa^msfH_&ea94}i1n zz{N~;!{liR`2GHtSS)rt6bd;f-BG(*7XW+^YX^7YcpUpAWkv!3!C>&tX_#Mo0l|B) zzLA~!4+G&TV`mUh2kfm8@E)-60)Te|cTZ!$jnj@lji45+4XBpH+Vka3w&5O`2K@bi zYky7zY=PZ?F`YHebzlPxz-!a!dJgc+IByn$Z^0@FY6b5E+G~Sqo>spDtM01R@g@*F z?fh9P>A@<6r`uh(+3q$cA~x{bunxASle--VoKemk6t)5TW>8jLk%w)vajeq@kKMgk z<#2}$dMj{r1~~?BAK;x8t~pxG2YenFJ%0@mXD?zMkyK_mez#3>)~h)mpB1)}t+NC* zn|=uR0^pxBp0fx(fEZSadu1M=v$hdd19kJoaZdhfU{|aSz;dh<8wD-`7Gt^HIPeaT z!3x?Pz=)iBVmEi)=Yqhrf-O?ja$N%E`OtOV2wbT4KgZ(vu>DKRg8%>k07*qoL + id="defs4"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/orchestra/static/orchestra/icons/order.svg b/orchestra/static/orchestra/icons/order.svg index 5daa110b..60accb4d 100644 --- a/orchestra/static/orchestra/icons/order.svg +++ b/orchestra/static/orchestra/icons/order.svg @@ -1577,6 +1577,59 @@ style="stop-color:#eeeeec;stop-opacity:0" id="stop2711-8" /> + + + + + image/svg+xml - + @@ -1613,178 +1666,173 @@ id="layer1" inkscape:label="Layer 1" inkscape:groupmode="layer"> + + + + + + + id="g8099" + transform="matrix(0.93883963,0,0,0.93883963,-946.56186,-295.71302)"> - - + + + + + + + + + + + + + + style="fill:#2e5c02;fill-opacity:1;fill-rule:evenodd;stroke:none" + id="rect2325-6" + y="360.97729" + x="1011.6381" + height="1.179504" + width="27.128592" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - + style="fill:#2e5c02;fill-opacity:1;fill-rule:evenodd;stroke:none" + id="rect2327-0" + y="358.61829" + x="1011.6381" + height="1.179504" + width="27.128592" /> + + + + + + + + diff --git a/orchestra/static/orchestra/icons/transaction.png b/orchestra/static/orchestra/icons/transaction.png index 24df7ee921f6eab440491c4c0af9dd8a83761e69..37c87998293e4bdef41160eb642c3c5dd699e865 100644 GIT binary patch delta 3243 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 de&D!S{0{>zz@V|�>xd002ovPDHLkV1fa)Dx3fS delta 4029 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 diff --git a/orchestra/static/orchestra/icons/transaction.svg b/orchestra/static/orchestra/icons/transaction.svg index 15156998..39b67d76 100644 --- a/orchestra/static/orchestra/icons/transaction.svg +++ b/orchestra/static/orchestra/icons/transaction.svg @@ -681,17 +681,6 @@ style="stop-color:white;stop-opacity:0" id="stop4226-3-7" /> - - - + + + + + + + + + + inkscape:window-width="1920" + inkscape:window-height="1024" + inkscape:window-x="0" + inkscape:window-y="27" + inkscape:window-maximized="1" /> @@ -920,498 +951,161 @@ inkscape:label="Layer 1" inkscape:groupmode="layer"> + inkscape:label="Layer 1" + id="layer1-1" + transform="matrix(0.93914302,0,0,0.95061485,-0.82457823,-2.3789835)"> + + + + + + + transform="matrix(0.88523756,0,0,0.88523756,-890.27694,-275.44544)" + id="g8099"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + d="m 1057.0679,360.95209 a 5.9014757,2.946741 0 1 1 -11.8029,0 5.9014757,2.946741 0 1 1 11.8029,0 z" + id="path2773-8" + style="fill:#ddca10;fill-opacity:1;fill-rule:evenodd;stroke:#7d6c0f;stroke-width:1.17950475;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> + + + + + + + + + + + transform="matrix(0.88523756,0,0,0.88523756,-879.08976,-274.46989)" + id="g8085"> + d="m 1011.048,356.79288 28.3084,0 0,7.10679 -28.3084,0 0,-7.10679 z" + id="path2321-7" + style="fill:#4f7f21;fill-opacity:1;fill-rule:evenodd;stroke:#2e5c02;stroke-width:1.17950368;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" /> + d="m 1014.3813,349.72956 21.4339,0 3.5411,7.08818 -28.3081,0 3.3331,-7.08818 z" + id="path2323-9" + style="fill:#59af05;fill-opacity:1;fill-rule:evenodd;stroke:#2e5c02;stroke-width:1.17950404;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" /> + + + d="m 1015.1028,350.96975 -2.3221,4.6443 24.5855,0 -2.4698,-4.6443 -19.7936,0 z" + id="path2329-4" + style="opacity:0.25;fill:none;stroke:#ffffff;stroke-width:1.17950404;stroke-miterlimit:4;stroke-opacity:1" /> + d="m 1031.6895,353.26726 a 5.89755,1.7692638 0 0 1 -11.7951,0 5.89755,1.7692638 0 1 1 11.7951,0 z" + id="path2746-1" + style="fill:#2e5c02;fill-opacity:1;fill-rule:evenodd;stroke:none" /> + + + + + +