From a2dbdfe794ebc738581568fd10620d0eaec4aecf Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 18 May 2019 15:45:32 +1000 Subject: [PATCH 01/23] Remove 'single_price' field from supplier part - Instead we will rely entirely on the SupplierPriceBreak model --- .../migrations/0014_auto_20190518_1543.py | 19 ++++++++++++++++ InvenTree/part/forms.py | 1 - .../0026_remove_supplierpart_single_price.py | 17 ++++++++++++++ InvenTree/part/models.py | 22 +++++++------------ 4 files changed, 44 insertions(+), 15 deletions(-) create mode 100644 InvenTree/build/migrations/0014_auto_20190518_1543.py create mode 100644 InvenTree/part/migrations/0026_remove_supplierpart_single_price.py diff --git a/InvenTree/build/migrations/0014_auto_20190518_1543.py b/InvenTree/build/migrations/0014_auto_20190518_1543.py new file mode 100644 index 0000000000..9fbd095445 --- /dev/null +++ b/InvenTree/build/migrations/0014_auto_20190518_1543.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2 on 2019-05-18 05:43 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('build', '0013_build_take_from'), + ] + + operations = [ + migrations.AlterField( + model_name='build', + name='take_from', + field=models.ForeignKey(blank=True, help_text='Select location to take stock from for this build (leave blank to take from any stock location)', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sourcing_builds', to='stock.StockLocation'), + ), + ] diff --git a/InvenTree/part/forms.py b/InvenTree/part/forms.py index d4e70ee47a..988cb7aa38 100644 --- a/InvenTree/part/forms.py +++ b/InvenTree/part/forms.py @@ -155,7 +155,6 @@ class EditSupplierPartForm(HelperForm): 'MPN', 'URL', 'note', - 'single_price', 'base_cost', 'multiple', 'minimum', diff --git a/InvenTree/part/migrations/0026_remove_supplierpart_single_price.py b/InvenTree/part/migrations/0026_remove_supplierpart_single_price.py new file mode 100644 index 0000000000..f0bde887a4 --- /dev/null +++ b/InvenTree/part/migrations/0026_remove_supplierpart_single_price.py @@ -0,0 +1,17 @@ +# Generated by Django 2.2 on 2019-05-18 05:43 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('part', '0025_auto_20190515_0012'), + ] + + operations = [ + migrations.RemoveField( + model_name='supplierpart', + name='single_price', + ), + ] diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index f4673dc309..1aa52342a9 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -816,7 +816,6 @@ class SupplierPart(models.Model): URL: Link to external website for this part description: Descriptive notes field note: Longer form note field - single_price: Default price for a single unit base_cost: Base charge added to order independent of quantity e.g. "Reeling Fee" multiple: Multiple that the part is provided in minimum: MOQ (minimum order quantity) required for purchase @@ -854,8 +853,6 @@ class SupplierPart(models.Model): note = models.CharField(max_length=100, blank=True, help_text='Notes') - single_price = models.DecimalField(max_digits=10, decimal_places=3, default=0, validators=[MinValueValidator(0)], help_text='Price for single quantity') - base_cost = models.DecimalField(max_digits=10, decimal_places=3, default=0, validators=[MinValueValidator(0)], help_text='Minimum charge (e.g. stocking fee)') packaging = models.CharField(max_length=50, blank=True, help_text='Part packaging') @@ -884,27 +881,26 @@ class SupplierPart(models.Model): def get_price(self, quantity, moq=True, multiples=True): """ Calculate the supplier price based on quantity price breaks. - - - If no price breaks available, use the single_price field + - Don't forget to add in flat-fee cost (base_cost field) - If MOQ (minimum order quantity) is required, bump quantity - If order multiples are to be observed, then we need to calculate based on that, too """ - # Order multiples - if multiples: - quantity = int(math.ceil(quantity / self.multipe) * self.multiple) - # Minimum ordering requirement if moq and self.minimum > quantity: quantity = self.minimum + # Order multiples + if multiples: + quantity = int(math.ceil(quantity / self.multipe) * self.multiple) + pb_found = False pb_quantity = -1 pb_cost = 0.0 for pb in self.price_breaks.all(): - # Ignore this pricebreak! + # Ignore this pricebreak (quantity is too high) if pb.quantity > quantity: continue @@ -915,13 +911,11 @@ class SupplierPart(models.Model): pb_quantity = pb.quantity pb_cost = pb.cost - # No appropriate price-break found - use the single cost! if pb_found: cost = pb_cost * quantity + return cost + self.base_cost else: - cost = self.single_price * quantity - - return cost + self.base_cost + return None def __str__(self): s = "{supplier} ({sku})".format( From 613c96e0939875e5ed72d3357a48695eb5d8a058 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 18 May 2019 16:20:48 +1000 Subject: [PATCH 02/23] Part attachment comment field is required --- .../part/migrations/0027_auto_20190518_1620.py | 18 ++++++++++++++++++ InvenTree/part/models.py | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 InvenTree/part/migrations/0027_auto_20190518_1620.py diff --git a/InvenTree/part/migrations/0027_auto_20190518_1620.py b/InvenTree/part/migrations/0027_auto_20190518_1620.py new file mode 100644 index 0000000000..85ed3e6be2 --- /dev/null +++ b/InvenTree/part/migrations/0027_auto_20190518_1620.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2 on 2019-05-18 06:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('part', '0026_remove_supplierpart_single_price'), + ] + + operations = [ + migrations.AlterField( + model_name='partattachment', + name='comment', + field=models.CharField(help_text='File comment', max_length=100), + ), + ] diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 1aa52342a9..54ca522cb0 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -644,7 +644,7 @@ class PartAttachment(models.Model): attachment = models.FileField(upload_to=attach_file, null=True, blank=True, help_text='Select file to attach') - comment = models.CharField(max_length=100, blank=True, help_text='File comment') + comment = models.CharField(max_length=100, help_text='File comment') @property def basename(self): From a1461de29706e8d14a4221620a77bd7a28bed59d Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 18 May 2019 16:25:32 +1000 Subject: [PATCH 03/23] Add supplierpricebreak model to the admin interface --- InvenTree/part/admin.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/InvenTree/part/admin.py b/InvenTree/part/admin.py index 18e20cb9ee..751c9176a5 100644 --- a/InvenTree/part/admin.py +++ b/InvenTree/part/admin.py @@ -4,6 +4,7 @@ from import_export.admin import ImportExportModelAdmin from .models import PartCategory, Part from .models import PartAttachment, PartStar from .models import SupplierPart +from .models import SupplierPriceBreak from .models import BomItem @@ -35,6 +36,10 @@ class SupplierPartAdmin(ImportExportModelAdmin): list_display = ('part', 'supplier', 'SKU') +class SupplierPriceBreakAdmin(ImportExportModelAdmin): + list_display = ('part', 'quantity', 'cost') + + """ class ParameterTemplateAdmin(admin.ModelAdmin): list_display = ('name', 'units', 'format') @@ -50,3 +55,4 @@ admin.site.register(PartAttachment, PartAttachmentAdmin) admin.site.register(PartStar, PartStarAdmin) admin.site.register(BomItem, BomItemAdmin) admin.site.register(SupplierPart, SupplierPartAdmin) +admin.site.register(SupplierPriceBreak, SupplierPriceBreakAdmin) From 92632b2ef7586758b08cf156fb7088f10337b3f3 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 18 May 2019 16:28:15 +1000 Subject: [PATCH 04/23] PartAttachment attachment file is actually required! --- .../migrations/0028_auto_20190518_1627.py | 19 +++++++++++++++++++ InvenTree/part/models.py | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 InvenTree/part/migrations/0028_auto_20190518_1627.py diff --git a/InvenTree/part/migrations/0028_auto_20190518_1627.py b/InvenTree/part/migrations/0028_auto_20190518_1627.py new file mode 100644 index 0000000000..fa0fa1457c --- /dev/null +++ b/InvenTree/part/migrations/0028_auto_20190518_1627.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2 on 2019-05-18 06:27 + +from django.db import migrations, models +import part.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('part', '0027_auto_20190518_1620'), + ] + + operations = [ + migrations.AlterField( + model_name='partattachment', + name='attachment', + field=models.FileField(help_text='Select file to attach', upload_to=part.models.attach_file), + ), + ] diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 54ca522cb0..34f9069f91 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -641,7 +641,7 @@ class PartAttachment(models.Model): part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='attachments') - attachment = models.FileField(upload_to=attach_file, null=True, blank=True, + attachment = models.FileField(upload_to=attach_file, help_text='Select file to attach') comment = models.CharField(max_length=100, help_text='File comment') From a3cd54875c92898cbbb47af31cfaa4229378578f Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 18 May 2019 16:32:48 +1000 Subject: [PATCH 05/23] Update validator for supplier price break --- .../migrations/0029_auto_20190518_1632.py | 19 +++++++++++++++++++ InvenTree/part/models.py | 3 +-- 2 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 InvenTree/part/migrations/0029_auto_20190518_1632.py diff --git a/InvenTree/part/migrations/0029_auto_20190518_1632.py b/InvenTree/part/migrations/0029_auto_20190518_1632.py new file mode 100644 index 0000000000..a5a2d61809 --- /dev/null +++ b/InvenTree/part/migrations/0029_auto_20190518_1632.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2 on 2019-05-18 06:32 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('part', '0028_auto_20190518_1627'), + ] + + operations = [ + migrations.AlterField( + model_name='supplierpricebreak', + name='quantity', + field=models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(1)]), + ), + ] diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 34f9069f91..42080ba408 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -941,8 +941,7 @@ class SupplierPriceBreak(models.Model): part = models.ForeignKey(SupplierPart, on_delete=models.CASCADE, related_name='price_breaks') - # At least 2 units are required for a 'price break' - Otherwise, just use single-price! - quantity = models.PositiveIntegerField(validators=[MinValueValidator(2)]) + quantity = models.PositiveIntegerField(validators=[MinValueValidator(1)]) cost = models.DecimalField(max_digits=10, decimal_places=3, validators=[MinValueValidator(0)]) From 1163f60b235c18c6b215dd9d485dcbe8d87b6f93 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 18 May 2019 16:42:15 +1000 Subject: [PATCH 06/23] Return price breaks in the correct order --- .../migrations/0030_auto_20190518_1641.py | 19 +++++++++++++++++++ InvenTree/part/models.py | 10 +++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 InvenTree/part/migrations/0030_auto_20190518_1641.py diff --git a/InvenTree/part/migrations/0030_auto_20190518_1641.py b/InvenTree/part/migrations/0030_auto_20190518_1641.py new file mode 100644 index 0000000000..7661dd6412 --- /dev/null +++ b/InvenTree/part/migrations/0030_auto_20190518_1641.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2 on 2019-05-18 06:41 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('part', '0029_auto_20190518_1632'), + ] + + operations = [ + migrations.AlterField( + model_name='supplierpricebreak', + name='part', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pricebreaks', to='part.SupplierPart'), + ), + ] diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 42080ba408..9c5476674a 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -879,6 +879,11 @@ class SupplierPart(models.Model): def has_price_breaks(self): return self.price_breaks.count() > 0 + @property + def price_breaks(self): + """ Return the associated price breaks in the correct order """ + return self.pricebreaks.order_by('quantity').all() + def get_price(self, quantity, moq=True, multiples=True): """ Calculate the supplier price based on quantity price breaks. @@ -939,7 +944,7 @@ class SupplierPriceBreak(models.Model): cost: Cost at specified quantity """ - part = models.ForeignKey(SupplierPart, on_delete=models.CASCADE, related_name='price_breaks') + part = models.ForeignKey(SupplierPart, on_delete=models.CASCADE, related_name='pricebreaks') quantity = models.PositiveIntegerField(validators=[MinValueValidator(1)]) @@ -949,8 +954,7 @@ class SupplierPriceBreak(models.Model): unique_together = ("part", "quantity") def __str__(self): - return "{mpn} - {cost}{currency} @ {quan}".format( + return "{mpn} - {cost} @ {quan}".format( mpn=self.part.MPN, cost=self.cost, - currency=self.currency if self.currency else '', quan=self.quantity) From fc3072a459fe9fe84810136b13cb889f16cd4be7 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 18 May 2019 16:42:57 +1000 Subject: [PATCH 07/23] Form for creating a new price break for a supplier part --- InvenTree/InvenTree/urls.py | 2 + InvenTree/InvenTree/views.py | 2 + .../company/templates/company/partdetail.html | 42 ++++++++++---- InvenTree/part/forms.py | 13 +++++ InvenTree/part/urls.py | 7 +++ InvenTree/part/views.py | 56 +++++++++++++++++++ 6 files changed, 112 insertions(+), 10 deletions(-) diff --git a/InvenTree/InvenTree/urls.py b/InvenTree/InvenTree/urls.py index 2872096c08..ee03848ae6 100644 --- a/InvenTree/InvenTree/urls.py +++ b/InvenTree/InvenTree/urls.py @@ -14,6 +14,7 @@ from company.urls import company_urls from part.urls import part_urls from part.urls import supplier_part_urls +from part.urls import price_break_urls from stock.urls import stock_urls @@ -50,6 +51,7 @@ apipatterns = [ urlpatterns = [ url(r'^part/', include(part_urls)), url(r'^supplier-part/', include(supplier_part_urls)), + url(r'^price-break/', include(price_break_urls)), url(r'^stock/', include(stock_urls)), diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index 9d5e1a2dc4..f6fdbc4d46 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -223,6 +223,7 @@ class AjaxCreateView(AjaxMixin, CreateView): super(CreateView, self).get(request, *args, **kwargs) + self.request = request form = self.get_form() return self.renderJsonResponse(request, form) @@ -233,6 +234,7 @@ class AjaxCreateView(AjaxMixin, CreateView): - If valid, save form - Return status info (success / failure) """ + self.request = request form = self.get_form() # Extra JSON data sent alongside form diff --git a/InvenTree/company/templates/company/partdetail.html b/InvenTree/company/templates/company/partdetail.html index b2fdf8aeb9..ba336fa3c5 100644 --- a/InvenTree/company/templates/company/partdetail.html +++ b/InvenTree/company/templates/company/partdetail.html @@ -10,7 +10,7 @@ InvenTree | {{ company.name }} - Parts

Supplier Part

-

{{ part.SKU }} - {{ part.supplier.name }}

+

{{ part.supplier.name }} - {{ part.SKU }}

@@ -59,29 +59,41 @@ InvenTree | {{ company.name }} - Parts
- - - {% if part.multiple > 1 %} + + + - {% endif %} {% if part.base_cost > 0 %} {% endif %} {% if part.minimum > 1 %} {% endif %} - {% if part.price_breaks.all %} - + + + + + {% if part.price_breaks.all %} {% for pb in part.price_breaks.all %} {% endfor %} + {% else %} + + + {% endif %}
Pricing
Single Price{{ part.single_price }}
Pricing
Order Multiple{{ part.multiple }}
Base Price (Flat Fee){{ part.base_cost }}
Minimum Order Quantity{{ part.minimum }}
Price Breaks
Price Breaks +
+ +
+
Quantity Price
{{ pb.quantity }} {{ pb.cost }}
+ No price breaks have been added for this part +
@@ -89,9 +101,7 @@ InvenTree | {{ company.name }} - Parts
-
- -
+ {% include 'modals.html' %} @@ -116,4 +126,16 @@ InvenTree | {{ company.name }} - Parts } ); }); + + $('#new-price-break').click(function() { + launchModalForm("{% url 'price-break-create' %}", + { + reload: true, + data: { + part: {{ part.id }}, + } + } + ); + }); + {% endblock %} \ No newline at end of file diff --git a/InvenTree/part/forms.py b/InvenTree/part/forms.py index 988cb7aa38..96fec2a597 100644 --- a/InvenTree/part/forms.py +++ b/InvenTree/part/forms.py @@ -12,6 +12,7 @@ from django import forms from .models import Part, PartCategory, PartAttachment from .models import BomItem from .models import SupplierPart +from .models import SupplierPriceBreak class PartImageForm(HelperForm): @@ -161,3 +162,15 @@ class EditSupplierPartForm(HelperForm): 'packaging', 'lead_time' ] + + +class EditPriceBreakForm(HelperForm): + """ Form for creating / editing a supplier price break """ + + class Meta: + model = SupplierPriceBreak + fields = [ + 'part', + 'quantity', + 'cost' + ] \ No newline at end of file diff --git a/InvenTree/part/urls.py b/InvenTree/part/urls.py index 0671240e73..0d02c01918 100644 --- a/InvenTree/part/urls.py +++ b/InvenTree/part/urls.py @@ -12,6 +12,13 @@ from django.conf.urls import url, include from . import views +price_break_urls = [ + url('^new/', views.PriceBreakCreate.as_view(), name='price-break-create'), + + url(r'^(?P\d+)/edit/', views.PriceBreakEdit.as_view(), name='price-break-edit'), + url(r'^(?P\d+)/delete/', views.PriceBreakDelete.as_view(), name='price-break-delete'), +] + supplier_part_detail_urls = [ url(r'edit/?', views.SupplierPartEdit.as_view(), name='supplier-part-edit'), url(r'delete/?', views.SupplierPartDelete.as_view(), name='supplier-part-delete'), diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index 55482d3284..7fa693ecdc 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -16,6 +16,7 @@ from company.models import Company from .models import PartCategory, Part, PartAttachment from .models import BomItem from .models import SupplierPart +from .models import SupplierPriceBreak from .models import match_part_names from . import forms as part_forms @@ -809,3 +810,58 @@ class SupplierPartDelete(AjaxDeleteView): ajax_template_name = 'company/partdelete.html' ajax_form_title = 'Delete Supplier Part' context_object_name = 'supplier_part' + + +class PriceBreakCreate(AjaxCreateView): + """ View for creating a supplier price break """ + + model = SupplierPriceBreak + form_class = part_forms.EditPriceBreakForm + ajax_form_title = 'Add Price Break' + ajax_template_name = 'modal_form.html' + + def get_data(self): + return { + 'success': 'Added new price break' + } + + def get_part(self): + try: + return SupplierPart.objects.get(id=self.request.GET.get('part')) + except SupplierPart.DoesNotExist: + return SupplierPart.objects.get(id=self.request.POST.get('part')) + + def get_form(self): + + form = super(AjaxCreateView, self).get_form() + + form.fields['part'].widget = HiddenInput() + + return form + + def get_initial(self): + + initials = super(AjaxCreateView, self).get_initial() + + print("GETTING INITIAL DAtA") + + initials['part'] = self.get_part() + + return initials + + +class PriceBreakEdit(AjaxUpdateView): + """ View for editing a supplier price break """ + + model = SupplierPriceBreak + form_class = part_forms.EditPriceBreakForm + ajax_form_title = 'Edit Price Break' + ajax_template_name = 'modal_form.html' + + +class PriceBreakDelete(AjaxDeleteView): + """ View for deleting a supplier price break """ + + model = SupplierPriceBreak + ajax_form_title = "Delete Price Break" + ajax_template_name = 'modal_delete_form.html' From 5043c354b1428f3826bf4e7fa94d04d69d9406f6 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 18 May 2019 17:35:39 +1000 Subject: [PATCH 08/23] Set default value for SupplierPriceBreak Edit or Delete a SupplierPriceBreak --- .../company/templates/company/partdetail.html | 27 ++++++++++++++++++- .../migrations/0031_auto_20190518_1650.py | 19 +++++++++++++ InvenTree/part/models.py | 8 +++++- InvenTree/part/views.py | 8 +++++- InvenTree/static/css/inventree.css | 4 +-- 5 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 InvenTree/part/migrations/0031_auto_20190518_1650.py diff --git a/InvenTree/company/templates/company/partdetail.html b/InvenTree/company/templates/company/partdetail.html index ba336fa3c5..5d0eee89a9 100644 --- a/InvenTree/company/templates/company/partdetail.html +++ b/InvenTree/company/templates/company/partdetail.html @@ -85,7 +85,12 @@ InvenTree | {{ company.name }} - Parts {% for pb in part.price_breaks.all %} {{ pb.quantity }} - {{ pb.cost }} + {{ pb.cost }} +
+ + +
+ {% endfor %} {% else %} @@ -138,4 +143,24 @@ InvenTree | {{ company.name }} - Parts ); }); + $('.pb-edit-button').click(function() { + var button = $(this); + + launchModalForm(button.attr('url'), + { + reload: true, + } + ); + }); + + $('.pb-delete-button').click(function() { + var button = $(this); + + launchModalForm(button.attr('url'), + { + reload: true, + } + ); + }); + {% endblock %} \ No newline at end of file diff --git a/InvenTree/part/migrations/0031_auto_20190518_1650.py b/InvenTree/part/migrations/0031_auto_20190518_1650.py new file mode 100644 index 0000000000..67c6d6124f --- /dev/null +++ b/InvenTree/part/migrations/0031_auto_20190518_1650.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2 on 2019-05-18 06:50 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('part', '0030_auto_20190518_1641'), + ] + + operations = [ + migrations.AlterField( + model_name='supplierpricebreak', + name='quantity', + field=models.PositiveIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1)]), + ), + ] diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 9c5476674a..ad210c7330 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -892,6 +892,12 @@ class SupplierPart(models.Model): - If order multiples are to be observed, then we need to calculate based on that, too """ + price_breaks = self.price_breaks.all() + + # No price break information available? + if len(price_breaks) == 0: + return None + # Minimum ordering requirement if moq and self.minimum > quantity: quantity = self.minimum @@ -946,7 +952,7 @@ class SupplierPriceBreak(models.Model): part = models.ForeignKey(SupplierPart, on_delete=models.CASCADE, related_name='pricebreaks') - quantity = models.PositiveIntegerField(validators=[MinValueValidator(1)]) + quantity = models.PositiveIntegerField(default=1, validators=[MinValueValidator(1)]) cost = models.DecimalField(max_digits=10, decimal_places=3, validators=[MinValueValidator(0)]) diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index 7fa693ecdc..61dffe986b 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -834,7 +834,6 @@ class PriceBreakCreate(AjaxCreateView): def get_form(self): form = super(AjaxCreateView, self).get_form() - form.fields['part'].widget = HiddenInput() return form @@ -858,6 +857,13 @@ class PriceBreakEdit(AjaxUpdateView): ajax_form_title = 'Edit Price Break' ajax_template_name = 'modal_form.html' + def get_form(self): + + form = super(AjaxUpdateView, self).get_form() + form.fields['part'].widget = HiddenInput() + + return form + class PriceBreakDelete(AjaxDeleteView): """ View for deleting a supplier price break """ diff --git a/InvenTree/static/css/inventree.css b/InvenTree/static/css/inventree.css index 59e7899891..4ef4f8789c 100644 --- a/InvenTree/static/css/inventree.css +++ b/InvenTree/static/css/inventree.css @@ -3,11 +3,11 @@ } .glyphicon { - font-size: 20px; + font-size: 18px; } .glyphicon-small { - font-size: 14px; + font-size: 12px; } .starred-part { From 0cfb243eb3355f14d4a8df50f6e7ecce16367835 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 18 May 2019 18:04:25 +1000 Subject: [PATCH 09/23] Move SupplierPart and SupplierPriceBreak to the 'Company' app - https://docs.djangoproject.com/en/2.2/ref/models/options/#django.db.models.Options.db_table - https://stackoverflow.com/questions/3519143/django-how-to-specify-a-database-for-a-model - And others, presumably --- InvenTree/InvenTree/urls.py | 4 +- InvenTree/company/admin.py | 12 ++ InvenTree/company/api.py | 3 + InvenTree/company/forms.py | 36 ++++ .../0007_supplierpart_supplierpricebreak.py | 52 ++++++ InvenTree/company/models.py | 173 ++++++++++++++++++ InvenTree/company/serializers.py | 43 +++++ InvenTree/company/urls.py | 20 ++ InvenTree/company/views.py | 145 +++++++++++++++ InvenTree/part/__init__.py | 1 - InvenTree/part/admin.py | 13 +- InvenTree/part/api.py | 6 +- InvenTree/part/forms.py | 36 ---- .../migrations/0032_auto_20190518_1759.py | 34 ++++ InvenTree/part/models.py | 169 +---------------- InvenTree/part/serializers.py | 42 +---- InvenTree/part/urls.py | 20 -- InvenTree/part/views.py | 141 -------------- .../migrations/0017_auto_20190518_1759.py | 19 ++ InvenTree/stock/models.py | 3 +- inventree_db.sqlite3.backup | Bin 0 -> 548864 bytes 21 files changed, 550 insertions(+), 422 deletions(-) create mode 100644 InvenTree/company/migrations/0007_supplierpart_supplierpricebreak.py create mode 100644 InvenTree/part/migrations/0032_auto_20190518_1759.py create mode 100644 InvenTree/stock/migrations/0017_auto_20190518_1759.py create mode 100644 inventree_db.sqlite3.backup diff --git a/InvenTree/InvenTree/urls.py b/InvenTree/InvenTree/urls.py index ee03848ae6..1bffd90229 100644 --- a/InvenTree/InvenTree/urls.py +++ b/InvenTree/InvenTree/urls.py @@ -11,10 +11,10 @@ from django.contrib.auth import views as auth_views from qr_code import urls as qr_code_urls from company.urls import company_urls +from company.urls import supplier_part_urls +from company.urls import price_break_urls from part.urls import part_urls -from part.urls import supplier_part_urls -from part.urls import price_break_urls from stock.urls import stock_urls diff --git a/InvenTree/company/admin.py b/InvenTree/company/admin.py index cbeff65cf8..25dbd1c5b6 100644 --- a/InvenTree/company/admin.py +++ b/InvenTree/company/admin.py @@ -2,10 +2,22 @@ from django.contrib import admin from import_export.admin import ImportExportModelAdmin from .models import Company +from .models import SupplierPart +from .models import SupplierPriceBreak class CompanyAdmin(ImportExportModelAdmin): list_display = ('name', 'website', 'contact') +class SupplierPartAdmin(ImportExportModelAdmin): + list_display = ('part', 'supplier', 'SKU') + + +class SupplierPriceBreakAdmin(ImportExportModelAdmin): + list_display = ('part', 'quantity', 'cost') + + admin.site.register(Company, CompanyAdmin) +admin.site.register(SupplierPart, SupplierPartAdmin) +admin.site.register(SupplierPriceBreak, SupplierPriceBreakAdmin) \ No newline at end of file diff --git a/InvenTree/company/api.py b/InvenTree/company/api.py index dfa3ebfb77..bf2f417f3b 100644 --- a/InvenTree/company/api.py +++ b/InvenTree/company/api.py @@ -12,7 +12,10 @@ from rest_framework import generics, permissions from django.conf.urls import url from .models import Company +from .models import SupplierPart, SupplierPriceBreak + from .serializers import CompanySerializer +from .serializers import SupplierPartSerializer, SupplierPriceBreakSerializer class CompanyList(generics.ListCreateAPIView): diff --git a/InvenTree/company/forms.py b/InvenTree/company/forms.py index 679fe323ee..0ad63b9d63 100644 --- a/InvenTree/company/forms.py +++ b/InvenTree/company/forms.py @@ -8,6 +8,8 @@ from __future__ import unicode_literals from InvenTree.forms import HelperForm from .models import Company +from .models import SupplierPart +from .models import SupplierPriceBreak class EditCompanyForm(HelperForm): @@ -37,3 +39,37 @@ class CompanyImageForm(HelperForm): fields = [ 'image' ] + + +class EditSupplierPartForm(HelperForm): + """ Form for editing a SupplierPart object """ + + class Meta: + model = SupplierPart + fields = [ + 'part', + 'supplier', + 'SKU', + 'description', + 'manufacturer', + 'MPN', + 'URL', + 'note', + 'base_cost', + 'multiple', + 'minimum', + 'packaging', + 'lead_time' + ] + + +class EditPriceBreakForm(HelperForm): + """ Form for creating / editing a supplier price break """ + + class Meta: + model = SupplierPriceBreak + fields = [ + 'part', + 'quantity', + 'cost' + ] diff --git a/InvenTree/company/migrations/0007_supplierpart_supplierpricebreak.py b/InvenTree/company/migrations/0007_supplierpart_supplierpricebreak.py new file mode 100644 index 0000000000..f3cc3ea6de --- /dev/null +++ b/InvenTree/company/migrations/0007_supplierpart_supplierpricebreak.py @@ -0,0 +1,52 @@ +# Generated by Django 2.2 on 2019-05-18 07:59 + +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('part', '0032_auto_20190518_1759'), + ('company', '0006_auto_20190508_2332'), + ] + + operations = [ + migrations.CreateModel( + name='SupplierPart', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('SKU', models.CharField(help_text='Supplier stock keeping unit', max_length=100)), + ('manufacturer', models.CharField(blank=True, help_text='Manufacturer', max_length=100)), + ('MPN', models.CharField(blank=True, help_text='Manufacturer part number', max_length=100)), + ('URL', models.URLField(blank=True, help_text='URL for external supplier part link')), + ('description', models.CharField(blank=True, help_text='Supplier part description', max_length=250)), + ('note', models.CharField(blank=True, help_text='Notes', max_length=100)), + ('base_cost', models.DecimalField(decimal_places=3, default=0, help_text='Minimum charge (e.g. stocking fee)', max_digits=10, validators=[django.core.validators.MinValueValidator(0)])), + ('packaging', models.CharField(blank=True, help_text='Part packaging', max_length=50)), + ('multiple', models.PositiveIntegerField(default=1, help_text='Order multiple', validators=[django.core.validators.MinValueValidator(1)])), + ('minimum', models.PositiveIntegerField(default=1, help_text='Minimum order quantity (MOQ)', validators=[django.core.validators.MinValueValidator(1)])), + ('lead_time', models.DurationField(blank=True, null=True)), + ('part', models.ForeignKey(help_text='Select part', limit_choices_to={'purchaseable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='supplier_parts', to='part.Part')), + ('supplier', models.ForeignKey(help_text='Select supplier', limit_choices_to={'is_supplier': True}, on_delete=django.db.models.deletion.CASCADE, related_name='parts', to='company.Company')), + ], + options={ + 'db_table': 'part_supplierpart', + 'unique_together': {('part', 'supplier', 'SKU')}, + }, + ), + migrations.CreateModel( + name='SupplierPriceBreak', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('quantity', models.PositiveIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1)])), + ('cost', models.DecimalField(decimal_places=3, max_digits=10, validators=[django.core.validators.MinValueValidator(0)])), + ('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pricebreaks', to='company.SupplierPart')), + ], + options={ + 'db_table': 'part_supplierpricebreak', + 'unique_together': {('part', 'quantity')}, + }, + ), + ] diff --git a/InvenTree/company/models.py b/InvenTree/company/models.py index 63fcf4add8..06aa100f26 100644 --- a/InvenTree/company/models.py +++ b/InvenTree/company/models.py @@ -7,6 +7,8 @@ from __future__ import unicode_literals import os +from django.core.validators import MinValueValidator + from django.apps import apps from django.db import models from django.urls import reverse @@ -150,3 +152,174 @@ class Contact(models.Model): company = models.ForeignKey(Company, related_name='contacts', on_delete=models.CASCADE) + + +class SupplierPart(models.Model): + """ Represents a unique part as provided by a Supplier + Each SupplierPart is identified by a MPN (Manufacturer Part Number) + Each SupplierPart is also linked to a Part object. + A Part may be available from multiple suppliers + + Attributes: + part: Link to the master Part + supplier: Company that supplies this SupplierPart object + SKU: Stock keeping unit (supplier part number) + manufacturer: Manufacturer name + MPN: Manufacture part number + URL: Link to external website for this part + description: Descriptive notes field + note: Longer form note field + base_cost: Base charge added to order independent of quantity e.g. "Reeling Fee" + multiple: Multiple that the part is provided in + minimum: MOQ (minimum order quantity) required for purchase + lead_time: Supplier lead time + packaging: packaging that the part is supplied in, e.g. "Reel" + """ + + def get_absolute_url(self): + return reverse('supplier-part-detail', kwargs={'pk': self.id}) + + class Meta: + unique_together = ('part', 'supplier', 'SKU') + + # This model was moved from the 'Part' app + db_table = 'part_supplierpart' + + part = models.ForeignKey('part.Part', on_delete=models.CASCADE, + related_name='supplier_parts', + limit_choices_to={'purchaseable': True}, + help_text='Select part', + ) + + supplier = models.ForeignKey(Company, on_delete=models.CASCADE, + related_name='parts', + limit_choices_to={'is_supplier': True}, + help_text='Select supplier', + ) + + SKU = models.CharField(max_length=100, help_text='Supplier stock keeping unit') + + manufacturer = models.CharField(max_length=100, blank=True, help_text='Manufacturer') + + MPN = models.CharField(max_length=100, blank=True, help_text='Manufacturer part number') + + URL = models.URLField(blank=True, help_text='URL for external supplier part link') + + description = models.CharField(max_length=250, blank=True, help_text='Supplier part description') + + note = models.CharField(max_length=100, blank=True, help_text='Notes') + + base_cost = models.DecimalField(max_digits=10, decimal_places=3, default=0, validators=[MinValueValidator(0)], help_text='Minimum charge (e.g. stocking fee)') + + packaging = models.CharField(max_length=50, blank=True, help_text='Part packaging') + + multiple = models.PositiveIntegerField(default=1, validators=[MinValueValidator(1)], help_text='Order multiple') + + minimum = models.PositiveIntegerField(default=1, validators=[MinValueValidator(1)], help_text='Minimum order quantity (MOQ)') + + lead_time = models.DurationField(blank=True, null=True) + + @property + def manufacturer_string(self): + + items = [] + + if self.manufacturer: + items.append(self.manufacturer) + if self.MPN: + items.append(self.MPN) + + return ' | '.join(items) + + @property + def has_price_breaks(self): + return self.price_breaks.count() > 0 + + @property + def price_breaks(self): + """ Return the associated price breaks in the correct order """ + return self.pricebreaks.order_by('quantity').all() + + def get_price(self, quantity, moq=True, multiples=True): + """ Calculate the supplier price based on quantity price breaks. + + - Don't forget to add in flat-fee cost (base_cost field) + - If MOQ (minimum order quantity) is required, bump quantity + - If order multiples are to be observed, then we need to calculate based on that, too + """ + + price_breaks = self.price_breaks.all() + + # No price break information available? + if len(price_breaks) == 0: + return None + + # Minimum ordering requirement + if moq and self.minimum > quantity: + quantity = self.minimum + + # Order multiples + if multiples: + quantity = int(math.ceil(quantity / self.multipe) * self.multiple) + + pb_found = False + pb_quantity = -1 + pb_cost = 0.0 + + for pb in self.price_breaks.all(): + # Ignore this pricebreak (quantity is too high) + if pb.quantity > quantity: + continue + + pb_found = True + + # If this price-break quantity is the largest so far, use it! + if pb.quantity > pb_quantity: + pb_quantity = pb.quantity + pb_cost = pb.cost + + if pb_found: + cost = pb_cost * quantity + return cost + self.base_cost + else: + return None + + def __str__(self): + s = "{supplier} ({sku})".format( + sku=self.SKU, + supplier=self.supplier.name) + + if self.manufacturer_string: + s = s + ' - ' + self.manufacturer_string + + return s + + +class SupplierPriceBreak(models.Model): + """ Represents a quantity price break for a SupplierPart. + - Suppliers can offer discounts at larger quantities + - SupplierPart(s) may have zero-or-more associated SupplierPriceBreak(s) + + Attributes: + part: Link to a SupplierPart object that this price break applies to + quantity: Quantity required for price break + cost: Cost at specified quantity + """ + + part = models.ForeignKey(SupplierPart, on_delete=models.CASCADE, related_name='pricebreaks') + + quantity = models.PositiveIntegerField(default=1, validators=[MinValueValidator(1)]) + + cost = models.DecimalField(max_digits=10, decimal_places=3, validators=[MinValueValidator(0)]) + + class Meta: + unique_together = ("part", "quantity") + + # This model was moved from the 'Part' app + db_table = 'part_supplierpricebreak' + + def __str__(self): + return "{mpn} - {cost} @ {quan}".format( + mpn=self.part.MPN, + cost=self.cost, + quan=self.quantity) diff --git a/InvenTree/company/serializers.py b/InvenTree/company/serializers.py index 2967dbebd5..b09bccd234 100644 --- a/InvenTree/company/serializers.py +++ b/InvenTree/company/serializers.py @@ -5,6 +5,9 @@ JSON serializers for Company app from rest_framework import serializers from .models import Company +from .models import SupplierPart, SupplierPriceBreak + +from part.serializers import PartBriefSerializer class CompanyBriefSerializer(serializers.ModelSerializer): @@ -47,3 +50,43 @@ class CompanySerializer(serializers.ModelSerializer): 'is_supplier', 'part_count' ] + + +class SupplierPartSerializer(serializers.ModelSerializer): + """ Serializer for SupplierPart object """ + + url = serializers.CharField(source='get_absolute_url', read_only=True) + + part_detail = PartBriefSerializer(source='part', many=False, read_only=True) + + supplier_name = serializers.CharField(source='supplier.name', read_only=True) + supplier_logo = serializers.CharField(source='supplier.get_image_url', read_only=True) + + class Meta: + model = SupplierPart + fields = [ + 'pk', + 'url', + 'part', + 'part_detail', + 'supplier', + 'supplier_name', + 'supplier_logo', + 'SKU', + 'manufacturer', + 'MPN', + 'URL', + ] + + +class SupplierPriceBreakSerializer(serializers.ModelSerializer): + """ Serializer for SupplierPriceBreak object """ + + class Meta: + model = SupplierPriceBreak + fields = [ + 'pk', + 'part', + 'quantity', + 'cost' + ] diff --git a/InvenTree/company/urls.py b/InvenTree/company/urls.py index 0f8719fe01..5b914e6cd9 100644 --- a/InvenTree/company/urls.py +++ b/InvenTree/company/urls.py @@ -36,3 +36,23 @@ company_urls = [ # Redirect any other patterns url(r'^.*$', RedirectView.as_view(url='', permanent=False), name='company-index'), ] + +price_break_urls = [ + url('^new/', views.PriceBreakCreate.as_view(), name='price-break-create'), + + url(r'^(?P\d+)/edit/', views.PriceBreakEdit.as_view(), name='price-break-edit'), + url(r'^(?P\d+)/delete/', views.PriceBreakDelete.as_view(), name='price-break-delete'), +] + +supplier_part_detail_urls = [ + url(r'edit/?', views.SupplierPartEdit.as_view(), name='supplier-part-edit'), + url(r'delete/?', views.SupplierPartDelete.as_view(), name='supplier-part-delete'), + + url('^.*$', views.SupplierPartDetail.as_view(), name='supplier-part-detail'), +] + +supplier_part_urls = [ + url(r'^new/?', views.SupplierPartCreate.as_view(), name='supplier-part-create'), + + url(r'^(?P\d+)/', include(supplier_part_detail_urls)), +] \ No newline at end of file diff --git a/InvenTree/company/views.py b/InvenTree/company/views.py index 0630556946..6a45b8c2d6 100644 --- a/InvenTree/company/views.py +++ b/InvenTree/company/views.py @@ -8,12 +8,18 @@ from __future__ import unicode_literals from django.views.generic import DetailView, ListView +from django.forms import HiddenInput + from InvenTree.views import AjaxCreateView, AjaxUpdateView, AjaxDeleteView from .models import Company +from .models import SupplierPart +from .models import SupplierPriceBreak from .forms import EditCompanyForm from .forms import CompanyImageForm +from .forms import EditSupplierPartForm +from .forms import EditPriceBreakForm class CompanyIndex(ListView): @@ -104,3 +110,142 @@ class CompanyDelete(AjaxDeleteView): return { 'danger': 'Company was deleted', } + + +class SupplierPartDetail(DetailView): + """ Detail view for SupplierPart """ + model = SupplierPart + template_name = 'company/partdetail.html' + context_object_name = 'part' + queryset = SupplierPart.objects.all() + + +class SupplierPartEdit(AjaxUpdateView): + """ Update view for editing SupplierPart """ + + model = SupplierPart + context_object_name = 'part' + form_class = EditSupplierPartForm + ajax_template_name = 'modal_form.html' + ajax_form_title = 'Edit Supplier Part' + + +class SupplierPartCreate(AjaxCreateView): + """ Create view for making new SupplierPart """ + + model = SupplierPart + form_class = EditSupplierPartForm + ajax_template_name = 'modal_form.html' + ajax_form_title = 'Create new Supplier Part' + context_object_name = 'part' + + def get_form(self): + """ Create Form instance to create a new SupplierPart object. + Hide some fields if they are not appropriate in context + """ + form = super(AjaxCreateView, self).get_form() + + if form.initial.get('supplier', None): + # Hide the supplier field + form.fields['supplier'].widget = HiddenInput() + + if form.initial.get('part', None): + # Hide the part field + form.fields['part'].widget = HiddenInput() + + return form + + def get_initial(self): + """ Provide initial data for new SupplierPart: + + - If 'supplier_id' provided, pre-fill supplier field + - If 'part_id' provided, pre-fill part field + """ + initials = super(SupplierPartCreate, self).get_initial().copy() + + supplier_id = self.get_param('supplier') + part_id = self.get_param('part') + + if supplier_id: + try: + initials['supplier'] = Company.objects.get(pk=supplier_id) + except Company.DoesNotExist: + initials['supplier'] = None + + if part_id: + try: + initials['part'] = Part.objects.get(pk=part_id) + except Part.DoesNotExist: + initials['part'] = None + + return initials + + +class SupplierPartDelete(AjaxDeleteView): + """ Delete view for removing a SupplierPart """ + model = SupplierPart + success_url = '/supplier/' + ajax_template_name = 'company/partdelete.html' + ajax_form_title = 'Delete Supplier Part' + context_object_name = 'supplier_part' + + +class PriceBreakCreate(AjaxCreateView): + """ View for creating a supplier price break """ + + model = SupplierPriceBreak + form_class = EditPriceBreakForm + ajax_form_title = 'Add Price Break' + ajax_template_name = 'modal_form.html' + + def get_data(self): + return { + 'success': 'Added new price break' + } + + def get_part(self): + try: + return SupplierPart.objects.get(id=self.request.GET.get('part')) + except SupplierPart.DoesNotExist: + return SupplierPart.objects.get(id=self.request.POST.get('part')) + + def get_form(self): + + form = super(AjaxCreateView, self).get_form() + form.fields['part'].widget = HiddenInput() + + return form + + def get_initial(self): + + initials = super(AjaxCreateView, self).get_initial() + + print("GETTING INITIAL DAtA") + + initials['part'] = self.get_part() + + return initials + + +class PriceBreakEdit(AjaxUpdateView): + """ View for editing a supplier price break """ + + model = SupplierPriceBreak + form_class = EditPriceBreakForm + ajax_form_title = 'Edit Price Break' + ajax_template_name = 'modal_form.html' + + def get_form(self): + + form = super(AjaxUpdateView, self).get_form() + form.fields['part'].widget = HiddenInput() + + return form + + +class PriceBreakDelete(AjaxDeleteView): + """ View for deleting a supplier price break """ + + model = SupplierPriceBreak + ajax_form_title = "Delete Price Break" + ajax_template_name = 'modal_delete_form.html' diff --git a/InvenTree/part/__init__.py b/InvenTree/part/__init__.py index da73be7753..e5912481ff 100644 --- a/InvenTree/part/__init__.py +++ b/InvenTree/part/__init__.py @@ -5,6 +5,5 @@ It includes models for: - PartCategory - Part -- SupplierPart - BomItem """ diff --git a/InvenTree/part/admin.py b/InvenTree/part/admin.py index 751c9176a5..5705abaa61 100644 --- a/InvenTree/part/admin.py +++ b/InvenTree/part/admin.py @@ -3,8 +3,6 @@ from import_export.admin import ImportExportModelAdmin from .models import PartCategory, Part from .models import PartAttachment, PartStar -from .models import SupplierPart -from .models import SupplierPriceBreak from .models import BomItem @@ -32,14 +30,6 @@ class BomItemAdmin(ImportExportModelAdmin): list_display = ('part', 'sub_part', 'quantity') -class SupplierPartAdmin(ImportExportModelAdmin): - list_display = ('part', 'supplier', 'SKU') - - -class SupplierPriceBreakAdmin(ImportExportModelAdmin): - list_display = ('part', 'quantity', 'cost') - - """ class ParameterTemplateAdmin(admin.ModelAdmin): list_display = ('name', 'units', 'format') @@ -54,5 +44,4 @@ admin.site.register(PartCategory, PartCategoryAdmin) admin.site.register(PartAttachment, PartAttachmentAdmin) admin.site.register(PartStar, PartStarAdmin) admin.site.register(BomItem, BomItemAdmin) -admin.site.register(SupplierPart, SupplierPartAdmin) -admin.site.register(SupplierPriceBreak, SupplierPriceBreakAdmin) + diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 0973138b21..889fae8111 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -15,11 +15,13 @@ from rest_framework import generics, permissions from django.conf.urls import url, include from django.urls import reverse + +from company.models import SupplierPart, SupplierPriceBreak +from company.serializers import SupplierPartSerializer, SupplierPriceBreakSerializer + from .models import Part, PartCategory, BomItem, PartStar -from .models import SupplierPart, SupplierPriceBreak from .serializers import PartSerializer, BomItemSerializer -from .serializers import SupplierPartSerializer, SupplierPriceBreakSerializer from .serializers import CategorySerializer from .serializers import PartStarSerializer diff --git a/InvenTree/part/forms.py b/InvenTree/part/forms.py index 96fec2a597..7f8854489c 100644 --- a/InvenTree/part/forms.py +++ b/InvenTree/part/forms.py @@ -11,8 +11,6 @@ from django import forms from .models import Part, PartCategory, PartAttachment from .models import BomItem -from .models import SupplierPart -from .models import SupplierPriceBreak class PartImageForm(HelperForm): @@ -140,37 +138,3 @@ class EditBomItemForm(HelperForm): # Prevent editing of the part associated with this BomItem widgets = {'part': forms.HiddenInput()} - - -class EditSupplierPartForm(HelperForm): - """ Form for editing a SupplierPart object """ - - class Meta: - model = SupplierPart - fields = [ - 'part', - 'supplier', - 'SKU', - 'description', - 'manufacturer', - 'MPN', - 'URL', - 'note', - 'base_cost', - 'multiple', - 'minimum', - 'packaging', - 'lead_time' - ] - - -class EditPriceBreakForm(HelperForm): - """ Form for creating / editing a supplier price break """ - - class Meta: - model = SupplierPriceBreak - fields = [ - 'part', - 'quantity', - 'cost' - ] \ No newline at end of file diff --git a/InvenTree/part/migrations/0032_auto_20190518_1759.py b/InvenTree/part/migrations/0032_auto_20190518_1759.py new file mode 100644 index 0000000000..8a82961947 --- /dev/null +++ b/InvenTree/part/migrations/0032_auto_20190518_1759.py @@ -0,0 +1,34 @@ +# Generated by Django 2.2 on 2019-05-18 07:59 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('stock', '0017_auto_20190518_1759'), + ('part', '0031_auto_20190518_1650'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='supplierpricebreak', + unique_together=None, + ), + migrations.RemoveField( + model_name='supplierpricebreak', + name='part', + ), + migrations.AlterField( + model_name='part', + name='default_supplier', + field=models.ForeignKey(blank=True, help_text='Default supplier part', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_parts', to='company.SupplierPart'), + ), + migrations.DeleteModel( + name='SupplierPart', + ), + migrations.DeleteModel( + name='SupplierPriceBreak', + ), + ] diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index ad210c7330..62fb6e7553 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -32,7 +32,8 @@ import hashlib from InvenTree import helpers from InvenTree import validators from InvenTree.models import InvenTreeTree -from company.models import Company + +from company.models import SupplierPart class PartCategory(InvenTreeTree): @@ -317,7 +318,7 @@ class Part(models.Model): # Default to None if there are multiple suppliers to choose from return None - default_supplier = models.ForeignKey('part.SupplierPart', + default_supplier = models.ForeignKey(SupplierPart, on_delete=models.SET_NULL, blank=True, null=True, help_text='Default supplier part', @@ -800,167 +801,3 @@ class BomItem(models.Model): return base_quantity + self.get_overage_quantity(base_quantity) - -class SupplierPart(models.Model): - """ Represents a unique part as provided by a Supplier - Each SupplierPart is identified by a MPN (Manufacturer Part Number) - Each SupplierPart is also linked to a Part object. - A Part may be available from multiple suppliers - - Attributes: - part: Link to the master Part - supplier: Company that supplies this SupplierPart object - SKU: Stock keeping unit (supplier part number) - manufacturer: Manufacturer name - MPN: Manufacture part number - URL: Link to external website for this part - description: Descriptive notes field - note: Longer form note field - base_cost: Base charge added to order independent of quantity e.g. "Reeling Fee" - multiple: Multiple that the part is provided in - minimum: MOQ (minimum order quantity) required for purchase - lead_time: Supplier lead time - packaging: packaging that the part is supplied in, e.g. "Reel" - """ - - def get_absolute_url(self): - return reverse('supplier-part-detail', kwargs={'pk': self.id}) - - class Meta: - unique_together = ('part', 'supplier', 'SKU') - - part = models.ForeignKey(Part, on_delete=models.CASCADE, - related_name='supplier_parts', - limit_choices_to={'purchaseable': True}, - help_text='Select part', - ) - - supplier = models.ForeignKey(Company, on_delete=models.CASCADE, - related_name='parts', - limit_choices_to={'is_supplier': True}, - help_text='Select supplier', - ) - - SKU = models.CharField(max_length=100, help_text='Supplier stock keeping unit') - - manufacturer = models.CharField(max_length=100, blank=True, help_text='Manufacturer') - - MPN = models.CharField(max_length=100, blank=True, help_text='Manufacturer part number') - - URL = models.URLField(blank=True, help_text='URL for external supplier part link') - - description = models.CharField(max_length=250, blank=True, help_text='Supplier part description') - - note = models.CharField(max_length=100, blank=True, help_text='Notes') - - base_cost = models.DecimalField(max_digits=10, decimal_places=3, default=0, validators=[MinValueValidator(0)], help_text='Minimum charge (e.g. stocking fee)') - - packaging = models.CharField(max_length=50, blank=True, help_text='Part packaging') - - multiple = models.PositiveIntegerField(default=1, validators=[MinValueValidator(1)], help_text='Order multiple') - - minimum = models.PositiveIntegerField(default=1, validators=[MinValueValidator(1)], help_text='Minimum order quantity (MOQ)') - - lead_time = models.DurationField(blank=True, null=True) - - @property - def manufacturer_string(self): - - items = [] - - if self.manufacturer: - items.append(self.manufacturer) - if self.MPN: - items.append(self.MPN) - - return ' | '.join(items) - - @property - def has_price_breaks(self): - return self.price_breaks.count() > 0 - - @property - def price_breaks(self): - """ Return the associated price breaks in the correct order """ - return self.pricebreaks.order_by('quantity').all() - - def get_price(self, quantity, moq=True, multiples=True): - """ Calculate the supplier price based on quantity price breaks. - - - Don't forget to add in flat-fee cost (base_cost field) - - If MOQ (minimum order quantity) is required, bump quantity - - If order multiples are to be observed, then we need to calculate based on that, too - """ - - price_breaks = self.price_breaks.all() - - # No price break information available? - if len(price_breaks) == 0: - return None - - # Minimum ordering requirement - if moq and self.minimum > quantity: - quantity = self.minimum - - # Order multiples - if multiples: - quantity = int(math.ceil(quantity / self.multipe) * self.multiple) - - pb_found = False - pb_quantity = -1 - pb_cost = 0.0 - - for pb in self.price_breaks.all(): - # Ignore this pricebreak (quantity is too high) - if pb.quantity > quantity: - continue - - pb_found = True - - # If this price-break quantity is the largest so far, use it! - if pb.quantity > pb_quantity: - pb_quantity = pb.quantity - pb_cost = pb.cost - - if pb_found: - cost = pb_cost * quantity - return cost + self.base_cost - else: - return None - - def __str__(self): - s = "{supplier} ({sku})".format( - sku=self.SKU, - supplier=self.supplier.name) - - if self.manufacturer_string: - s = s + ' - ' + self.manufacturer_string - - return s - - -class SupplierPriceBreak(models.Model): - """ Represents a quantity price break for a SupplierPart. - - Suppliers can offer discounts at larger quantities - - SupplierPart(s) may have zero-or-more associated SupplierPriceBreak(s) - - Attributes: - part: Link to a SupplierPart object that this price break applies to - quantity: Quantity required for price break - cost: Cost at specified quantity - """ - - part = models.ForeignKey(SupplierPart, on_delete=models.CASCADE, related_name='pricebreaks') - - quantity = models.PositiveIntegerField(default=1, validators=[MinValueValidator(1)]) - - cost = models.DecimalField(max_digits=10, decimal_places=3, validators=[MinValueValidator(0)]) - - class Meta: - unique_together = ("part", "quantity") - - def __str__(self): - return "{mpn} - {cost} @ {quan}".format( - mpn=self.part.MPN, - cost=self.cost, - quan=self.quantity) diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index 87ca59c13b..947fa9904b 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -5,7 +5,7 @@ JSON serializers for Part app from rest_framework import serializers from .models import Part, PartStar -from .models import SupplierPart, SupplierPriceBreak + from .models import PartCategory from .models import BomItem @@ -119,43 +119,3 @@ class BomItemSerializer(InvenTreeModelSerializer): 'overage', 'note', ] - - -class SupplierPartSerializer(serializers.ModelSerializer): - """ Serializer for SupplierPart object """ - - url = serializers.CharField(source='get_absolute_url', read_only=True) - - part_detail = PartBriefSerializer(source='part', many=False, read_only=True) - - supplier_name = serializers.CharField(source='supplier.name', read_only=True) - supplier_logo = serializers.CharField(source='supplier.get_image_url', read_only=True) - - class Meta: - model = SupplierPart - fields = [ - 'pk', - 'url', - 'part', - 'part_detail', - 'supplier', - 'supplier_name', - 'supplier_logo', - 'SKU', - 'manufacturer', - 'MPN', - 'URL', - ] - - -class SupplierPriceBreakSerializer(serializers.ModelSerializer): - """ Serializer for SupplierPriceBreak object """ - - class Meta: - model = SupplierPriceBreak - fields = [ - 'pk', - 'part', - 'quantity', - 'cost' - ] diff --git a/InvenTree/part/urls.py b/InvenTree/part/urls.py index 0d02c01918..89453c63f9 100644 --- a/InvenTree/part/urls.py +++ b/InvenTree/part/urls.py @@ -12,26 +12,6 @@ from django.conf.urls import url, include from . import views -price_break_urls = [ - url('^new/', views.PriceBreakCreate.as_view(), name='price-break-create'), - - url(r'^(?P\d+)/edit/', views.PriceBreakEdit.as_view(), name='price-break-edit'), - url(r'^(?P\d+)/delete/', views.PriceBreakDelete.as_view(), name='price-break-delete'), -] - -supplier_part_detail_urls = [ - url(r'edit/?', views.SupplierPartEdit.as_view(), name='supplier-part-edit'), - url(r'delete/?', views.SupplierPartDelete.as_view(), name='supplier-part-delete'), - - url('^.*$', views.SupplierPartDetail.as_view(), name='supplier-part-detail'), -] - -supplier_part_urls = [ - url(r'^new/?', views.SupplierPartCreate.as_view(), name='supplier-part-create'), - - url(r'^(?P\d+)/', include(supplier_part_detail_urls)), -] - part_attachment_urls = [ url('^new/?', views.PartAttachmentCreate.as_view(), name='part-attachment-create'), url(r'^(?P\d+)/edit/?', views.PartAttachmentEdit.as_view(), name='part-attachment-edit'), diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index 61dffe986b..5cc57150e4 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -15,8 +15,6 @@ from django.forms import HiddenInput, CheckboxInput from company.models import Company from .models import PartCategory, Part, PartAttachment from .models import BomItem -from .models import SupplierPart -from .models import SupplierPriceBreak from .models import match_part_names from . import forms as part_forms @@ -732,142 +730,3 @@ class BomItemDelete(AjaxDeleteView): ajax_template_name = 'part/bom-delete.html' context_object_name = 'item' ajax_form_title = 'Confim BOM item deletion' - - -class SupplierPartDetail(DetailView): - """ Detail view for SupplierPart """ - model = SupplierPart - template_name = 'company/partdetail.html' - context_object_name = 'part' - queryset = SupplierPart.objects.all() - - -class SupplierPartEdit(AjaxUpdateView): - """ Update view for editing SupplierPart """ - - model = SupplierPart - context_object_name = 'part' - form_class = part_forms.EditSupplierPartForm - ajax_template_name = 'modal_form.html' - ajax_form_title = 'Edit Supplier Part' - - -class SupplierPartCreate(AjaxCreateView): - """ Create view for making new SupplierPart """ - - model = SupplierPart - form_class = part_forms.EditSupplierPartForm - ajax_template_name = 'modal_form.html' - ajax_form_title = 'Create new Supplier Part' - context_object_name = 'part' - - def get_form(self): - """ Create Form instance to create a new SupplierPart object. - Hide some fields if they are not appropriate in context - """ - form = super(AjaxCreateView, self).get_form() - - if form.initial.get('supplier', None): - # Hide the supplier field - form.fields['supplier'].widget = HiddenInput() - - if form.initial.get('part', None): - # Hide the part field - form.fields['part'].widget = HiddenInput() - - return form - - def get_initial(self): - """ Provide initial data for new SupplierPart: - - - If 'supplier_id' provided, pre-fill supplier field - - If 'part_id' provided, pre-fill part field - """ - initials = super(SupplierPartCreate, self).get_initial().copy() - - supplier_id = self.get_param('supplier') - part_id = self.get_param('part') - - if supplier_id: - try: - initials['supplier'] = Company.objects.get(pk=supplier_id) - except Company.DoesNotExist: - initials['supplier'] = None - - if part_id: - try: - initials['part'] = Part.objects.get(pk=part_id) - except Part.DoesNotExist: - initials['part'] = None - - return initials - - -class SupplierPartDelete(AjaxDeleteView): - """ Delete view for removing a SupplierPart """ - model = SupplierPart - success_url = '/supplier/' - ajax_template_name = 'company/partdelete.html' - ajax_form_title = 'Delete Supplier Part' - context_object_name = 'supplier_part' - - -class PriceBreakCreate(AjaxCreateView): - """ View for creating a supplier price break """ - - model = SupplierPriceBreak - form_class = part_forms.EditPriceBreakForm - ajax_form_title = 'Add Price Break' - ajax_template_name = 'modal_form.html' - - def get_data(self): - return { - 'success': 'Added new price break' - } - - def get_part(self): - try: - return SupplierPart.objects.get(id=self.request.GET.get('part')) - except SupplierPart.DoesNotExist: - return SupplierPart.objects.get(id=self.request.POST.get('part')) - - def get_form(self): - - form = super(AjaxCreateView, self).get_form() - form.fields['part'].widget = HiddenInput() - - return form - - def get_initial(self): - - initials = super(AjaxCreateView, self).get_initial() - - print("GETTING INITIAL DAtA") - - initials['part'] = self.get_part() - - return initials - - -class PriceBreakEdit(AjaxUpdateView): - """ View for editing a supplier price break """ - - model = SupplierPriceBreak - form_class = part_forms.EditPriceBreakForm - ajax_form_title = 'Edit Price Break' - ajax_template_name = 'modal_form.html' - - def get_form(self): - - form = super(AjaxUpdateView, self).get_form() - form.fields['part'].widget = HiddenInput() - - return form - - -class PriceBreakDelete(AjaxDeleteView): - """ View for deleting a supplier price break """ - - model = SupplierPriceBreak - ajax_form_title = "Delete Price Break" - ajax_template_name = 'modal_delete_form.html' diff --git a/InvenTree/stock/migrations/0017_auto_20190518_1759.py b/InvenTree/stock/migrations/0017_auto_20190518_1759.py new file mode 100644 index 0000000000..c0e583999e --- /dev/null +++ b/InvenTree/stock/migrations/0017_auto_20190518_1759.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2 on 2019-05-18 07:59 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('stock', '0016_auto_20190512_2119'), + ] + + operations = [ + migrations.AlterField( + model_name='stockitem', + name='supplier_part', + field=models.ForeignKey(blank=True, help_text='Select a matching supplier part for this stock item', null=True, on_delete=django.db.models.deletion.SET_NULL, to='company.SupplierPart'), + ), + ] diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 950d6237dd..2e48ea167f 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -22,6 +22,7 @@ from InvenTree import helpers from InvenTree.models import InvenTreeTree from part.models import Part +from company.models import SupplierPart class StockLocation(InvenTreeTree): @@ -188,7 +189,7 @@ class StockItem(models.Model): part = models.ForeignKey('part.Part', on_delete=models.CASCADE, related_name='locations', help_text='Base part') - supplier_part = models.ForeignKey('part.SupplierPart', blank=True, null=True, on_delete=models.SET_NULL, + supplier_part = models.ForeignKey(SupplierPart, blank=True, null=True, on_delete=models.SET_NULL, help_text='Select a matching supplier part for this stock item') location = models.ForeignKey(StockLocation, on_delete=models.DO_NOTHING, diff --git a/inventree_db.sqlite3.backup b/inventree_db.sqlite3.backup new file mode 100644 index 0000000000000000000000000000000000000000..0ec1ba7feba86fc2a6bd4ffc979d21b6bd2d6d24 GIT binary patch literal 548864 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

e=1PUQG>4h1Z^CA?s;j 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 Date: Sat, 18 May 2019 20:09:41 +1000 Subject: [PATCH 10/23] Display part pricing in Part view - Calculate min_price and max_price based on provided supplier information --- InvenTree/company/models.py | 4 +- InvenTree/part/models.py | 55 ++++++++++++++++++++ InvenTree/part/templates/part/part_base.html | 19 +++++++ 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/InvenTree/company/models.py b/InvenTree/company/models.py index 06aa100f26..b45bd04c3d 100644 --- a/InvenTree/company/models.py +++ b/InvenTree/company/models.py @@ -7,6 +7,8 @@ from __future__ import unicode_literals import os +import math + from django.core.validators import MinValueValidator from django.apps import apps @@ -260,7 +262,7 @@ class SupplierPart(models.Model): # Order multiples if multiples: - quantity = int(math.ceil(quantity / self.multipe) * self.multiple) + quantity = int(math.ceil(quantity / self.multiple) * self.multiple) pb_found = False pb_quantity = -1 diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 62fb6e7553..00fb07290e 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -563,6 +563,61 @@ class Part(models.Model): """ Return the number of supplier parts available for this part """ return self.supplier_parts.count() + @property + def min_single_price(self): + return self.get_min_supplier_price(1) + + @property + def max_single_price(self): + return self.get_max_supplier_price(1) + + def get_min_supplier_price(self, quantity=1): + """ Return the minimum price of this part from all available suppliers. + + Args: + quantity: Number of units we wish to purchase (default = 1) + + Returns: + Numerical price if pricing is available, else None + """ + + min_price = None + + for supplier_part in self.supplier_parts.all(): + supplier_price = supplier_part.get_price(quantity) + + if supplier_price is None: + continue + + if min_price is None or supplier_price < min_price: + min_price = supplier_price + + return min_price + + def get_max_supplier_price(self, quantity=1): + """ Return the maximum price of this part from all available suppliers. + + Args: + quantity: Number of units we wish to purchase (default = 1) + + Returns: + Numerical price if pricing is available, else None + """ + + max_price = None + + for supplier_part in self.supplier_parts.all(): + supplier_price = supplier_part.get_price(quantity) + + if supplier_price is None: + continue + + if max_price is None or supplier_price > max_price: + max_price = supplier_price + + return max_price + + def deepCopy(self, other, **kwargs): """ Duplicates non-field data from another part. Does not alter the normal fields of this part, diff --git a/InvenTree/part/templates/part/part_base.html b/InvenTree/part/templates/part/part_base.html index ea3bbc7617..405556bdfa 100644 --- a/InvenTree/part/templates/part/part_base.html +++ b/InvenTree/part/templates/part/part_base.html @@ -82,6 +82,25 @@ {{ part.allocation_count }} {% endif %} + {% if part.supplier_count > 0 %} + + + Price + + + {% if part.min_single_price %} + {% if part.min_single_price == part.max_single_price %} + {{ part.min_single_price }} + {% else %} + {{ part.min_single_price }} to {{ part.max_single_price }} + {% endif %} + from {{ part.supplier_count }} suppliers. + {% else %} + No pricing data avilable + {% endif %} + + + {% endif %}

From cdc55bb5d3877858db8f214269799c91ea345383 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 18 May 2019 20:24:09 +1000 Subject: [PATCH 11/23] PEP fixes --- InvenTree/company/admin.py | 2 +- InvenTree/company/api.py | 80 ++++++++++++++++++++++++++++++++++++- InvenTree/company/urls.py | 2 +- InvenTree/company/views.py | 2 + InvenTree/part/admin.py | 1 - InvenTree/part/api.py | 82 +------------------------------------- InvenTree/part/models.py | 4 -- InvenTree/part/views.py | 3 +- 8 files changed, 87 insertions(+), 89 deletions(-) diff --git a/InvenTree/company/admin.py b/InvenTree/company/admin.py index 25dbd1c5b6..607f773198 100644 --- a/InvenTree/company/admin.py +++ b/InvenTree/company/admin.py @@ -20,4 +20,4 @@ class SupplierPriceBreakAdmin(ImportExportModelAdmin): admin.site.register(Company, CompanyAdmin) admin.site.register(SupplierPart, SupplierPartAdmin) -admin.site.register(SupplierPriceBreak, SupplierPriceBreakAdmin) \ No newline at end of file +admin.site.register(SupplierPriceBreak, SupplierPriceBreakAdmin) diff --git a/InvenTree/company/api.py b/InvenTree/company/api.py index bf2f417f3b..894974d3e9 100644 --- a/InvenTree/company/api.py +++ b/InvenTree/company/api.py @@ -9,7 +9,7 @@ from django_filters.rest_framework import DjangoFilterBackend from rest_framework import filters from rest_framework import generics, permissions -from django.conf.urls import url +from django.conf.urls import url, include from .models import Company from .models import SupplierPart, SupplierPriceBreak @@ -68,7 +68,85 @@ class CompanyDetail(generics.RetrieveUpdateDestroyAPIView): ] +class SupplierPartList(generics.ListCreateAPIView): + """ API endpoint for list view of SupplierPart object + + - GET: Return list of SupplierPart objects + - POST: Create a new SupplierPart object + """ + + queryset = SupplierPart.objects.all() + serializer_class = SupplierPartSerializer + + permission_classes = [ + permissions.IsAuthenticatedOrReadOnly, + ] + + filter_backends = [ + DjangoFilterBackend, + filters.SearchFilter, + filters.OrderingFilter, + ] + + filter_fields = [ + 'part', + 'supplier' + ] + + +class SupplierPartDetail(generics.RetrieveUpdateDestroyAPIView): + """ API endpoint for detail view of SupplierPart object + + - GET: Retrieve detail view + - PATCH: Update object + - DELETE: Delete objec + """ + + queryset = SupplierPart.objects.all() + serializer_class = SupplierPartSerializer + permission_classes = (permissions.IsAuthenticatedOrReadOnly,) + + read_only_fields = [ + ] + + +class SupplierPriceBreakList(generics.ListCreateAPIView): + """ API endpoint for list view of SupplierPriceBreak object + + - GET: Retrieve list of SupplierPriceBreak objects + - POST: Create a new SupplierPriceBreak object + """ + + queryset = SupplierPriceBreak.objects.all() + serializer_class = SupplierPriceBreakSerializer + + permission_classes = [ + permissions.IsAuthenticatedOrReadOnly, + ] + + filter_backends = [ + DjangoFilterBackend, + ] + + filter_fields = [ + 'part', + ] + + +supplier_part_api_urls = [ + + url(r'^(?P\d+)/?', SupplierPartDetail.as_view(), name='api-supplier-part-detail'), + + # Catch anything else + url(r'^.*$', SupplierPartList.as_view(), name='api-part-supplier-list'), +] + + company_api_urls = [ + + url(r'^part/', include(supplier_part_api_urls)), + + url(r'^price-break/?', SupplierPriceBreakList.as_view(), name='api-part-supplier-price'), url(r'^(?P\d+)/?', CompanyDetail.as_view(), name='api-company-detail'), diff --git a/InvenTree/company/urls.py b/InvenTree/company/urls.py index 5b914e6cd9..7f617baafd 100644 --- a/InvenTree/company/urls.py +++ b/InvenTree/company/urls.py @@ -55,4 +55,4 @@ supplier_part_urls = [ url(r'^new/?', views.SupplierPartCreate.as_view(), name='supplier-part-create'), url(r'^(?P\d+)/', include(supplier_part_detail_urls)), -] \ No newline at end of file +] diff --git a/InvenTree/company/views.py b/InvenTree/company/views.py index 6a45b8c2d6..cce518676a 100644 --- a/InvenTree/company/views.py +++ b/InvenTree/company/views.py @@ -16,6 +16,8 @@ from .models import Company from .models import SupplierPart from .models import SupplierPriceBreak +from part.models import Part + from .forms import EditCompanyForm from .forms import CompanyImageForm from .forms import EditSupplierPartForm diff --git a/InvenTree/part/admin.py b/InvenTree/part/admin.py index 5705abaa61..136d486b0b 100644 --- a/InvenTree/part/admin.py +++ b/InvenTree/part/admin.py @@ -44,4 +44,3 @@ admin.site.register(PartCategory, PartCategoryAdmin) admin.site.register(PartAttachment, PartAttachmentAdmin) admin.site.register(PartStar, PartStarAdmin) admin.site.register(BomItem, BomItemAdmin) - diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 889fae8111..aa065e9720 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -15,10 +15,6 @@ from rest_framework import generics, permissions from django.conf.urls import url, include from django.urls import reverse - -from company.models import SupplierPart, SupplierPriceBreak -from company.serializers import SupplierPartSerializer, SupplierPriceBreakSerializer - from .models import Part, PartCategory, BomItem, PartStar from .serializers import PartSerializer, BomItemSerializer @@ -235,71 +231,6 @@ class BomDetail(generics.RetrieveUpdateDestroyAPIView): ] -class SupplierPartList(generics.ListCreateAPIView): - """ API endpoint for list view of SupplierPart object - - - GET: Return list of SupplierPart objects - - POST: Create a new SupplierPart object - """ - - queryset = SupplierPart.objects.all() - serializer_class = SupplierPartSerializer - - permission_classes = [ - permissions.IsAuthenticatedOrReadOnly, - ] - - filter_backends = [ - DjangoFilterBackend, - filters.SearchFilter, - filters.OrderingFilter, - ] - - filter_fields = [ - 'part', - 'supplier' - ] - - -class SupplierPartDetail(generics.RetrieveUpdateDestroyAPIView): - """ API endpoint for detail view of SupplierPart object - - - GET: Retrieve detail view - - PATCH: Update object - - DELETE: Delete objec - """ - - queryset = SupplierPart.objects.all() - serializer_class = SupplierPartSerializer - permission_classes = (permissions.IsAuthenticatedOrReadOnly,) - - read_only_fields = [ - ] - - -class SupplierPriceBreakList(generics.ListCreateAPIView): - """ API endpoint for list view of SupplierPriceBreak object - - - GET: Retrieve list of SupplierPriceBreak objects - - POST: Create a new SupplierPriceBreak object - """ - - queryset = SupplierPriceBreak.objects.all() - serializer_class = SupplierPriceBreakSerializer - - permission_classes = [ - permissions.IsAuthenticatedOrReadOnly, - ] - - filter_backends = [ - DjangoFilterBackend, - ] - - filter_fields = [ - 'part', - ] - - cat_api_urls = [ url(r'^(?P\d+)/?', CategoryDetail.as_view(), name='api-part-category-detail'), @@ -307,13 +238,6 @@ cat_api_urls = [ url(r'^$', CategoryList.as_view(), name='api-part-category-list'), ] -supplier_part_api_urls = [ - - url(r'^(?P\d+)/?', SupplierPartDetail.as_view(), name='api-supplier-part-detail'), - - # Catch anything else - url(r'^.*$', SupplierPartList.as_view(), name='api-part-supplier-list'), -] part_star_api_urls = [ url(r'^(?P\d+)/?', PartStarDetail.as_view(), name='api-part-star-detail'), @@ -322,21 +246,19 @@ part_star_api_urls = [ url(r'^.*$', PartStarList.as_view(), name='api-part-star-list'), ] + part_api_urls = [ url(r'^tree/?', PartCategoryTree.as_view(), name='api-part-tree'), url(r'^category/', include(cat_api_urls)), - url(r'^supplier/', include(supplier_part_api_urls)), - url(r'^star/', include(part_star_api_urls)), - url(r'^price-break/?', SupplierPriceBreakList.as_view(), name='api-part-supplier-price'), - url(r'^(?P\d+)/', PartDetail.as_view(), name='api-part-detail'), url(r'^.*$', PartList.as_view(), name='api-part-list'), ] + bom_api_urls = [ # BOM Item Detail url('^(?P\d+)/', BomDetail.as_view(), name='api-bom-detail'), diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 00fb07290e..4313228498 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -7,8 +7,6 @@ from __future__ import unicode_literals import os -import math - import tablib from django.utils.translation import gettext_lazy as _ @@ -617,7 +615,6 @@ class Part(models.Model): return max_price - def deepCopy(self, other, **kwargs): """ Duplicates non-field data from another part. Does not alter the normal fields of this part, @@ -855,4 +852,3 @@ class BomItem(models.Model): base_quantity = self.quantity * build_quantity return base_quantity + self.get_overage_quantity(base_quantity) - diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index 5cc57150e4..1cdb4acf2d 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -12,11 +12,12 @@ from django.views.generic import DetailView, ListView from django.forms.models import model_to_dict from django.forms import HiddenInput, CheckboxInput -from company.models import Company from .models import PartCategory, Part, PartAttachment from .models import BomItem from .models import match_part_names +from company.models import SupplierPart + from . import forms as part_forms from InvenTree.views import AjaxView, AjaxCreateView, AjaxUpdateView, AjaxDeleteView From 4c82714777e57ebd01d770bfabba7c05fef244a0 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 18 May 2019 21:20:44 +1000 Subject: [PATCH 12/23] Allow 5 decimal places for pricing info --- .../migrations/0008_auto_20190518_2120.py | 19 +++++++++++++++++++ InvenTree/company/models.py | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 InvenTree/company/migrations/0008_auto_20190518_2120.py diff --git a/InvenTree/company/migrations/0008_auto_20190518_2120.py b/InvenTree/company/migrations/0008_auto_20190518_2120.py new file mode 100644 index 0000000000..ef9c318f52 --- /dev/null +++ b/InvenTree/company/migrations/0008_auto_20190518_2120.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2 on 2019-05-18 11:20 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('company', '0007_supplierpart_supplierpricebreak'), + ] + + operations = [ + migrations.AlterField( + model_name='supplierpricebreak', + name='cost', + field=models.DecimalField(decimal_places=5, max_digits=10, validators=[django.core.validators.MinValueValidator(0)]), + ), + ] diff --git a/InvenTree/company/models.py b/InvenTree/company/models.py index b45bd04c3d..90a0df23a2 100644 --- a/InvenTree/company/models.py +++ b/InvenTree/company/models.py @@ -312,7 +312,7 @@ class SupplierPriceBreak(models.Model): quantity = models.PositiveIntegerField(default=1, validators=[MinValueValidator(1)]) - cost = models.DecimalField(max_digits=10, decimal_places=3, validators=[MinValueValidator(0)]) + cost = models.DecimalField(max_digits=10, decimal_places=5, validators=[MinValueValidator(0)]) class Meta: unique_together = ("part", "quantity") From 2a1fd2b03b8efc190995fe87ef73c64a8fc998a5 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 18 May 2019 21:22:56 +1000 Subject: [PATCH 13/23] More complex pricing calculations - Calculate BOM price for a part - Calculate total pricing for a part (build or purchase) - Display pricing information in BOM table --- InvenTree/part/models.py | 139 ++++++++++++++++++++++- InvenTree/part/serializers.py | 2 + InvenTree/part/templates/part/bom.html | 8 ++ InvenTree/static/script/inventree/bom.js | 13 +++ 4 files changed, 161 insertions(+), 1 deletion(-) diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 4313228498..7025ae20ce 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -569,6 +569,49 @@ class Part(models.Model): def max_single_price(self): return self.get_max_supplier_price(1) + @property + def min_bom_price(self): + return self.get_min_bom_price(1) + + @property + def max_bom_price(self): + return self.get_max_bom_price(1) + + @property + def has_pricing_info(self): + """ Return true if there is pricing information for this part """ + return self.get_min_price() is not None + + @property + def has_complete_bom_pricing(self): + """ Return true if there is pricing information for each item in the BOM. """ + + for item in self.bom_items.all(): + if not item.sub_part.has_pricing_info: + return False + + return True + + @property + def single_price_info(self): + """ Return a simplified pricing string for this part at single quantity """ + + return self.get_price_info() + + def get_price_info(self, quantity=1): + """ Return a simplified pricing string for this part """ + + min_price = self.get_min_price(quantity) + max_price = self.get_max_price(quantity) + + if min_price is None: + return None + + if min_price == max_price: + return min_price + + return "{a} to {b}".format(a=min_price, b=max_price) + def get_min_supplier_price(self, quantity=1): """ Return the minimum price of this part from all available suppliers. @@ -590,7 +633,10 @@ class Part(models.Model): if min_price is None or supplier_price < min_price: min_price = supplier_price - return min_price + if min_price is None: + return None + else: + return min_price * quantity def get_max_supplier_price(self, quantity=1): """ Return the maximum price of this part from all available suppliers. @@ -613,8 +659,99 @@ class Part(models.Model): if max_price is None or supplier_price > max_price: max_price = supplier_price + if max_price is None: + return None + else: + return max_price * quantity + + def get_min_bom_price(self, quantity=1): + """ Return the minimum price of the BOM for this part. + Adds the minimum price for all components in the BOM. + + Note: If the BOM contains items without pricing information, + these items cannot be included in the BOM! + """ + + min_price = None + + for item in self.bom_items.all(): + price = item.sub_part.get_min_price(quantity * item.quantity) + + if price is None: + continue + + if min_price is None: + min_price = 0 + + min_price += price + + return min_price + + def get_max_bom_price(self, quantity=1): + """ Return the maximum price of the BOM for this part. + Adds the maximum price for all components in the BOM. + + Note: If the BOM contains items without pricing information, + these items cannot be included in the BOM! + """ + + max_price = None + + for item in self.bom_items.all(): + price = item.sub_part.get_max_price(quantity * item.quantity) + + if price is None: + continue + + if max_price is None: + max_price = 0 + + max_price += price + return max_price + def get_min_price(self, quantity=1): + """ Return the minimum price for this part. This price can be either: + + - Supplier price (if purchased from suppliers) + - BOM price (if built from other parts) + + Returns: + Minimum of the supplier price or BOM price. If no pricing available, returns None + """ + + buy_price = self.get_min_supplier_price(quantity) + bom_price = self.get_min_bom_price(quantity) + + if buy_price is None: + return bom_price + + if bom_price is None: + return buy_price + + return min(buy_price, bom_price) + + def get_max_price(self, quantity=1): + """ Return the maximum price for this part. This price can be either: + + - Supplier price (if purchsed from suppliers) + - BOM price (if built from other parts) + + Returns: + Maximum of the supplier price or BOM price. If no pricing available, returns None + """ + + buy_price = self.get_max_supplier_price(quantity) + bom_price = self.get_max_bom_price(quantity) + + if buy_price is None: + return bom_price + + if bom_price is None: + return buy_price + + return max(buy_price, bom_price) + def deepCopy(self, other, **kwargs): """ Duplicates non-field data from another part. Does not alter the normal fields of this part, diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index 947fa9904b..9415cdbfb0 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -34,6 +34,7 @@ class PartBriefSerializer(serializers.ModelSerializer): url = serializers.CharField(source='get_absolute_url', read_only=True) image_url = serializers.CharField(source='get_image_url', read_only=True) + single_price_info = serializers.CharField(read_only=True) class Meta: model = Part @@ -43,6 +44,7 @@ class PartBriefSerializer(serializers.ModelSerializer): 'full_name', 'description', 'available_stock', + 'single_price_info', 'image_url', ] diff --git a/InvenTree/part/templates/part/bom.html b/InvenTree/part/templates/part/bom.html index 3990bfab6c..bff082b6ae 100644 --- a/InvenTree/part/templates/part/bom.html +++ b/InvenTree/part/templates/part/bom.html @@ -11,6 +11,14 @@

Bill of Materials

+{% if part.has_complete_bom_pricing == False %} +
+ The BOM for {{ part.full_name }} does not have complete pricing information +
+{% endif %} +
+ Single BOM Price: {{ part.min_bom_price }} to {{ part.max_bom_price }} +
{% if part.bom_checked_date %} {% if part.is_bom_valid %}
diff --git a/InvenTree/static/script/inventree/bom.js b/InvenTree/static/script/inventree/bom.js index 6ff81de4fc..f71353fce7 100644 --- a/InvenTree/static/script/inventree/bom.js +++ b/InvenTree/static/script/inventree/bom.js @@ -151,6 +151,19 @@ function loadBomTable(table, options) { } } ); + + cols.push({ + field: 'sub_part_detail.single_price_info', + title: 'Price', + sortable: true, + formatter: function(value, row, index, field) { + if (value) { + return value; + } else { + return "No pricing information"; + } + }, + }); } // Part notes From 2b098942b0f40b2873c903843fecc10fca9f3b41 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 18 May 2019 21:43:52 +1000 Subject: [PATCH 14/23] Fix pricing bug - Include BOM item pricing in API --- InvenTree/part/models.py | 9 +++++++-- InvenTree/part/serializers.py | 2 ++ InvenTree/static/script/inventree/bom.js | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 7025ae20ce..536ed50798 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -636,7 +636,7 @@ class Part(models.Model): if min_price is None: return None else: - return min_price * quantity + return min_price def get_max_supplier_price(self, quantity=1): """ Return the maximum price of this part from all available suppliers. @@ -662,7 +662,7 @@ class Part(models.Model): if max_price is None: return None else: - return max_price * quantity + return max_price def get_min_bom_price(self, quantity=1): """ Return the minimum price of the BOM for this part. @@ -989,3 +989,8 @@ class BomItem(models.Model): base_quantity = self.quantity * build_quantity return base_quantity + self.get_overage_quantity(base_quantity) + + @property + def price_info(self): + """ Return the price for this item in the BOM """ + return self.sub_part.get_price_info(self.quantity) \ No newline at end of file diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index 9415cdbfb0..593f7003f9 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -108,6 +108,7 @@ class BomItemSerializer(InvenTreeModelSerializer): part_detail = PartBriefSerializer(source='part', many=False, read_only=True) sub_part_detail = PartBriefSerializer(source='sub_part', many=False, read_only=True) + price_info = serializers.CharField(read_only=True) class Meta: model = BomItem @@ -118,6 +119,7 @@ class BomItemSerializer(InvenTreeModelSerializer): 'sub_part', 'sub_part_detail', 'quantity', + 'price_info', 'overage', 'note', ] diff --git a/InvenTree/static/script/inventree/bom.js b/InvenTree/static/script/inventree/bom.js index f71353fce7..6e00eabe67 100644 --- a/InvenTree/static/script/inventree/bom.js +++ b/InvenTree/static/script/inventree/bom.js @@ -153,7 +153,7 @@ function loadBomTable(table, options) { ); cols.push({ - field: 'sub_part_detail.single_price_info', + field: 'price_info', title: 'Price', sortable: true, formatter: function(value, row, index, field) { From b64a29b8979e80364d87fc7e96b39aa8c6b9c10d Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 18 May 2019 21:56:00 +1000 Subject: [PATCH 15/23] Display build pricing information --- .../build/templates/build/build_base.html | 13 +++++++++ InvenTree/build/views.py | 10 +++++++ InvenTree/part/models.py | 28 +++++++++++-------- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/InvenTree/build/templates/build/build_base.html b/InvenTree/build/templates/build/build_base.html index 620db18df7..b2102ed74b 100644 --- a/InvenTree/build/templates/build/build_base.html +++ b/InvenTree/build/templates/build/build_base.html @@ -36,6 +36,19 @@ InvenTree | Build - {{ build }} Quantity {{ build.quantity }} + + BOM Price + + {% if bom_price %} + {{ bom_price }} + {% if build.part.has_complete_bom_pricing == False %} + BOM pricing is incomplete + {% endif %} + {% else %} + No pricing information + {% endif %} + +
diff --git a/InvenTree/build/views.py b/InvenTree/build/views.py index 6f19c67754..14e22a3fe8 100644 --- a/InvenTree/build/views.py +++ b/InvenTree/build/views.py @@ -265,6 +265,16 @@ class BuildDetail(DetailView): template_name = 'build/detail.html' context_object_name = 'build' + def get_context_data(self, **kwargs): + + ctx = super(DetailView, self).get_context_data(**kwargs) + + build = self.get_object() + + ctx['bom_price'] = build.part.get_price_info(build.quantity, buy=False) + + return ctx + class BuildAllocate(DetailView): """ View for allocating parts to a Build """ diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 536ed50798..a9278e0dec 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -598,11 +598,17 @@ class Part(models.Model): return self.get_price_info() - def get_price_info(self, quantity=1): - """ Return a simplified pricing string for this part """ - - min_price = self.get_min_price(quantity) - max_price = self.get_max_price(quantity) + def get_price_info(self, quantity=1, buy=True, bom=True): + """ Return a simplified pricing string for this part + + Args: + quantity: Number of units to calculate price for + buy: Include supplier pricing (default = True) + bom: Include BOM pricing (default = True) + """ + + min_price = self.get_min_price(quantity, buy, bom) + max_price = self.get_max_price(quantity, buy, bom) if min_price is None: return None @@ -710,7 +716,7 @@ class Part(models.Model): return max_price - def get_min_price(self, quantity=1): + def get_min_price(self, quantity=1, buy=True, bom=True): """ Return the minimum price for this part. This price can be either: - Supplier price (if purchased from suppliers) @@ -720,8 +726,8 @@ class Part(models.Model): Minimum of the supplier price or BOM price. If no pricing available, returns None """ - buy_price = self.get_min_supplier_price(quantity) - bom_price = self.get_min_bom_price(quantity) + buy_price = self.get_min_supplier_price(quantity) if buy else None + bom_price = self.get_min_bom_price(quantity) if bom else None if buy_price is None: return bom_price @@ -731,7 +737,7 @@ class Part(models.Model): return min(buy_price, bom_price) - def get_max_price(self, quantity=1): + def get_max_price(self, quantity=1, buy=True, bom=True): """ Return the maximum price for this part. This price can be either: - Supplier price (if purchsed from suppliers) @@ -741,8 +747,8 @@ class Part(models.Model): Maximum of the supplier price or BOM price. If no pricing available, returns None """ - buy_price = self.get_max_supplier_price(quantity) - bom_price = self.get_max_bom_price(quantity) + buy_price = self.get_max_supplier_price(quantity) if buy else None + bom_price = self.get_max_bom_price(quantity) if bom else None if buy_price is None: return bom_price From 54ccf6c7b3863a8a7cd55b4a219797bc928ebc84 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 18 May 2019 21:56:39 +1000 Subject: [PATCH 16/23] PEP --- InvenTree/part/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index a9278e0dec..d8b7529b23 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -999,4 +999,4 @@ class BomItem(models.Model): @property def price_info(self): """ Return the price for this item in the BOM """ - return self.sub_part.get_price_info(self.quantity) \ No newline at end of file + return self.sub_part.get_price_info(self.quantity) From dcf79338c1e7df4bf54a69ec5727fe17f36c1bed Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 18 May 2019 22:58:11 +1000 Subject: [PATCH 17/23] Add pop-up pricing window for part - Cost to purchase from suppliers - Cost to build from BOM --- InvenTree/part/forms.py | 16 ++++++ InvenTree/part/templates/part/part_base.html | 14 ++++- .../part/templates/part/part_pricing.html | 41 ++++++++++++++ InvenTree/part/urls.py | 1 + InvenTree/part/views.py | 56 +++++++++++++++++++ InvenTree/static/css/inventree.css | 6 +- 6 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 InvenTree/part/templates/part/part_pricing.html diff --git a/InvenTree/part/forms.py b/InvenTree/part/forms.py index 7f8854489c..e069df6ed6 100644 --- a/InvenTree/part/forms.py +++ b/InvenTree/part/forms.py @@ -138,3 +138,19 @@ class EditBomItemForm(HelperForm): # Prevent editing of the part associated with this BomItem widgets = {'part': forms.HiddenInput()} + + +class PartPriceForm(forms.Form): + """ Simple form for viewing part pricing information """ + + quantity = forms.IntegerField( + required=True, + initial=1, + help_text='Input quantity for price calculation' + ) + + class Meta: + model = Part + fields = [ + 'quantity' + ] diff --git a/InvenTree/part/templates/part/part_base.html b/InvenTree/part/templates/part/part_base.html index 405556bdfa..5e4f85d238 100644 --- a/InvenTree/part/templates/part/part_base.html +++ b/InvenTree/part/templates/part/part_base.html @@ -32,10 +32,13 @@

{{ part.description }}

- {% include "qr_button.html" %} + {% include "qr_button.html" %} +

@@ -141,6 +144,15 @@ ); }); + $("#price-button").click(function() { + launchModalForm( + "{% url 'part-pricing' part.id %}", + { + submit_text: 'Calculate', + } + ); + }); + $("#toggle-starred").click(function() { toggleStar({ part: {{ part.id }}, diff --git a/InvenTree/part/templates/part/part_pricing.html b/InvenTree/part/templates/part/part_pricing.html new file mode 100644 index 0000000000..2930d56ed0 --- /dev/null +++ b/InvenTree/part/templates/part/part_pricing.html @@ -0,0 +1,41 @@ +{% extends "modal_form.html" %} + +{% block pre_form_content %} + +
+Calculate pricing information for {{ part }}. +
+ +
+ + + + + + + + +{% if buy_price %} + + + + +{% endif %} +{% if bom_price %} + + + + + {% if part.has_complete_bom_pricing == false %} + + + + {% endif %} +{% endif %} +
Part{{ part }}
Quantity{{ quantity }}
Buy Cost{{ buy_price }}
BOM Cost{{ bom_price }}
+ BOM pricing is incomplete +
+ +
+ +{% endblock %} \ No newline at end of file diff --git a/InvenTree/part/urls.py b/InvenTree/part/urls.py index 89453c63f9..900ebe8127 100644 --- a/InvenTree/part/urls.py +++ b/InvenTree/part/urls.py @@ -24,6 +24,7 @@ part_detail_urls = [ url(r'^bom-export/?', views.BomDownload.as_view(), name='bom-export'), url(r'^validate-bom/', views.BomValidate.as_view(), name='bom-validate'), url(r'^duplicate/', views.PartDuplicate.as_view(), name='part-duplicate'), + url(r'^pricing/', views.PartPricing.as_view(), name='part-pricing'), url(r'^track/?', views.PartDetail.as_view(template_name='part/track.html'), name='part-track'), url(r'^attachments/?', views.PartDetail.as_view(template_name='part/attachments.html'), name='part-attachments'), diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index 1cdb4acf2d..7a7a85ae2d 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -551,6 +551,62 @@ class PartDelete(AjaxDeleteView): } +class PartPricing(AjaxView): + """ View for inspecting part pricing information """ + + model = Part + ajax_template_name = "part/part_pricing.html" + ajax_form_title = "Part Pricing" + form_class = part_forms.PartPriceForm + + def get_part(self): + try: + return Part.objects.get(id=self.kwargs['pk']) + except Part.DoesNotExist: + return None + + def get_pricing(self, quantity=1): + + part = self.get_part() + + ctx = { + 'part': part, + 'quantity': quantity + } + + if part is None: + return ctx + + buy_price = part.get_price_info(quantity, bom=False) + bom_price = part.get_price_info(quantity, buy=False) + + if buy_price: + ctx['buy_price'] = buy_price + + if bom_price: + ctx['bom_price'] = bom_price + + return ctx + + def get(self, request, *args, **kwargs): + + return self.renderJsonResponse(request, self.form_class(), context=self.get_pricing()) + + def post(self, request, *args, **kwargs): + + try: + quantity = int(self.request.POST.get('quantity', 1)) + except ValueError: + quantity = 1 + + # Always mark the form as 'invalid' (the user may wish to keep getting pricing data) + data = { + 'form_valid': False, + } + + return self.renderJsonResponse(request, self.form_class(), data=data, context=self.get_pricing(quantity)) + + class CategoryDetail(DetailView): """ Detail view for PartCategory """ model = PartCategory diff --git a/InvenTree/static/css/inventree.css b/InvenTree/static/css/inventree.css index 4ef4f8789c..ff7ac4e98b 100644 --- a/InvenTree/static/css/inventree.css +++ b/InvenTree/static/css/inventree.css @@ -11,7 +11,11 @@ } .starred-part { - color: #ffcc00; + color: #ffbb00; +} + +.part-price { + color: rgb(13, 245, 25); } /* CSS overrides for treeview */ From 368193d397f27254a1720a7709a7f4fce14d0400 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 18 May 2019 23:01:59 +1000 Subject: [PATCH 18/23] Fix formatting --- InvenTree/part/templates/part/part_pricing.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InvenTree/part/templates/part/part_pricing.html b/InvenTree/part/templates/part/part_pricing.html index 2930d56ed0..6dd54fd495 100644 --- a/InvenTree/part/templates/part/part_pricing.html +++ b/InvenTree/part/templates/part/part_pricing.html @@ -26,10 +26,10 @@ Calculate pricing information for {{ part }}. BOM Cost {{ bom_price }} - {% if part.has_complete_bom_pricing == false %} + {% if part.has_complete_bom_pricing == False %} - BOM pricing is incomplete + Note: BOM pricing is incomplete for this part {% endif %} From ffda5a1b295f1394bf47f259f91441d091f96947 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 18 May 2019 23:13:18 +1000 Subject: [PATCH 19/23] Add warning if no pricing data available --- InvenTree/part/templates/part/part_pricing.html | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/InvenTree/part/templates/part/part_pricing.html b/InvenTree/part/templates/part/part_pricing.html index 6dd54fd495..bfff341049 100644 --- a/InvenTree/part/templates/part/part_pricing.html +++ b/InvenTree/part/templates/part/part_pricing.html @@ -15,13 +15,13 @@ Calculate pricing information for {{ part }}. Quantity {{ quantity }} -{% if buy_price %} + {% if buy_price %} Buy Cost {{ buy_price }} -{% endif %} -{% if bom_price %} + {% endif %} + {% if bom_price %} BOM Cost {{ bom_price }} @@ -33,9 +33,16 @@ Calculate pricing information for {{ part }}. {% endif %} -{% endif %} + {% endif %} +{% if buy_price or bom_price %} +{% else %} +
+ No pricing information is available for this part. +
+{% endif %} +
{% endblock %} \ No newline at end of file From a54760b21964701b61076392e6085dea976c1227 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 18 May 2019 23:44:43 +1000 Subject: [PATCH 20/23] Improve pricing display --- .../part/templates/part/part_pricing.html | 60 +++++++++++++++---- InvenTree/part/views.py | 30 ++++++++-- 2 files changed, 74 insertions(+), 16 deletions(-) diff --git a/InvenTree/part/templates/part/part_pricing.html b/InvenTree/part/templates/part/part_pricing.html index bfff341049..6d82549bb2 100644 --- a/InvenTree/part/templates/part/part_pricing.html +++ b/InvenTree/part/templates/part/part_pricing.html @@ -6,37 +6,77 @@ Calculate pricing information for {{ part }}. +

Quantity

- + - + - {% if buy_price %} +
Part{{ part }}{{ part }}
Quantity{{ quantity }}{{ quantity }}
+ {% if part.supplier_count > 0 %} +

Supplier Pricing

+ + {% if min_total_buy_price %} - - + + + + + {% if quantity > 1 %} + + + + {% endif %} - {% if bom_price %} + {% else %} - - + + {% endif %} +
Buy Cost{{ buy_price }}Unit CostMin: {{ min_unit_buy_price }}Max: {{ max_unit_buy_price }}
Total CostMin: {{ min_total_buy_price }}Max: {{ max_total_buy_price }}
BOM Cost{{ bom_price }} + No supplier pricing available +
+ {% endif %} + + {% if part.bom_count > 0 %} +

BOM Pricing

+ + {% if min_total_bom_price %} + + + + + + {% if quantity > 1 %} + + + + + + {% endif %} {% if part.has_complete_bom_pricing == False %} - {% endif %} + {% else %} + + + {% endif %}
Unit CostMin: {{ min_unit_bom_price }}Max: {{ max_unit_bom_price }}
Total CostMin: {{ min_total_bom_price }}Max: {{ max_total_bom_price }}
+ Note: BOM pricing is incomplete for this part
+ No BOM pricing available +
+ {% endif %} -{% if buy_price or bom_price %} +{% if min_unit_buy_price or min_unit_bom_price %} {% else %}
No pricing information is available for this part. diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index 7a7a85ae2d..d5824b2dab 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -577,14 +577,32 @@ class PartPricing(AjaxView): if part is None: return ctx - buy_price = part.get_price_info(quantity, bom=False) - bom_price = part.get_price_info(quantity, buy=False) + # Supplier pricing information + if part.supplier_count > 0: + min_buy_price = part.get_min_supplier_price(quantity) + max_buy_price = part.get_max_supplier_price(quantity) - if buy_price: - ctx['buy_price'] = buy_price + if min_buy_price: + ctx['min_total_buy_price'] = min_buy_price + ctx['min_unit_buy_price'] = min_buy_price / quantity - if bom_price: - ctx['bom_price'] = bom_price + if max_buy_price: + ctx['max_total_buy_price'] = max_buy_price + ctx['max_unit_buy_price'] = max_buy_price / quantity + + # BOM pricing information + if part.bom_count > 0: + + min_bom_price = part.get_min_bom_price(quantity) + max_bom_price = part.get_max_bom_price(quantity) + + if min_bom_price: + ctx['min_total_bom_price'] = min_bom_price + ctx['min_unit_bom_price'] = min_bom_price / quantity + + if max_bom_price: + ctx['max_total_bom_price'] = max_bom_price + ctx['max_unit_bom_price'] = max_bom_price / quantity return ctx From f88e26cd5cff24e5b6886ddffbd161cdcbc46773 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 18 May 2019 23:48:03 +1000 Subject: [PATCH 21/23] Remove backup database file --- .gitignore | 1 + inventree_db.sqlite3.backup | Bin 548864 -> 0 bytes 2 files changed, 1 insertion(+) delete mode 100644 inventree_db.sqlite3.backup diff --git a/.gitignore b/.gitignore index 6229f979df..bfbaf7c285 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ var/ *.log local_settings.py *.sqlite3 +*.backup # Sphinx files docs/_build diff --git a/inventree_db.sqlite3.backup b/inventree_db.sqlite3.backup deleted file mode 100644 index 0ec1ba7feba86fc2a6bd4ffc979d21b6bd2d6d24..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 548864 zcmeFa2YegHl|R0VLSRvL#U@IkWI>c@iKakq#R8yJz$VfxqDWB91p!Hjf&>y20IJw= z61l{Q9Xma7?6{;mm*kQ-{pU0%aq1<`r8hg(|6F=^dhY*bW`SK4(uyzn+?{V|>+N9X zy*F>Z({^TO_e~ubO&3yuW7*t7vJg<1H4Mu#djkQ6@pdu{^KAH+hJP-GvHd4p9q_OC z#})%CUqMILx=%WRp-y-dv1Nom3y%xG6n-rHKzP*kJMPtWA92n*UTFWZ?XR}|?2DMs zudM6BQqJ#baJ8Havgh;Z#kthk1(ZD*hjn+Nu^ zb)1jZyISTW?D?cw0+PCv$}Obx`E+(MpD@Fy5Mgyzn~lgRY*`g^gHZ~r`Bh9SXtWq# zC4!1jW(OGGxyIFUB^W=pn(;&v*gYf3;_S?^NNv08FUamHWlv;sQ2?wxA-G%4+av6C zTQA7mvce|B**QhlW<;~)R3Wrx<@Qs>Yc|PMvbo^0L6a&|1BKnjL)CPaRgHyZHoI6z zEfx}mb4w}Iq~v3&9!jY*xN1y3p+=pUsfm@eSnM{rQTwS705hlju9kD1>~(@!C8kJK z2D90@)M9cWl@O)mjG}8vvo=gAe&uQ~nJ!TE1(yVBplDoM$90E&u9jGgy`iU&oXL=W zihDmMrV)8_Y^hA~8-<9+dPfJW<4GkWf)xgmKpHA^K9viM?~ez@r$lMn{hr zkpulzjj-CLD(xl~7nVoIb)whR5)868G*&>1dzb}K^4qr(j3oJg6nJEexUgEmycY=B z^_Ot2md%^lXKyWstkn2CD}eN*&{<06^JlU-XpyIrx!L2%+zv?&ca)kXG?Gj*Ur1!K z^XbL5z+AGBDx?=u*7S5fkza;}ff{LBU?!W*SW{6f8UjrHifF1g=5EO4SULyUtjt+7 zWL1vTLNc8(bJ8_4Cy+;m7Lvz~RT~K@$=O2sG+0?_KPqwJL^i#c0*I=L-ywPGS_U?= z3?2^ir!sK6mVk-^ez?CuA>#%|Dh0jQjk{efkqCQ(Zq_epOJ?sP9iu9it5Ne+Oi!vA z5H0`k#tf*Ng+ez_|BNx_{F*vf%a$$d`I8lZV$5;LTq^A-+@ZMYTJ~*O zwOJ6vNxQ&dH{3&`f6R5;oUWG6PWJj|n&mN3#Dy|{*3~GPnfebXo@OZ?RA=Qa!Ri${ zcOtnspDmlb4U?XDaw^Tw=Ex*kZ0R^Zdt7dq6lA%NMeP0>ZM z=h8A^sY+OdrL<9MEUZdVN$JuhF(gYpJx?I4R0u0lRE>1$nh2dU8nqT#tIAk5i;Odt z0WP&m*CV2R`;0| zg=#EWC?sc(FQ8Q}v=($FnX5rX49E(Y4f%ITvLq_uo={olx+QZ}jwEF0Vnty&5|HGm zra^smh2=oz2!lJ4p+3z#>`iQOYv5 zRu-|kwy+G%Hv$?!xTDmuRHI`p=Q8ESO5v!Yb%nwaNe^Etfy}Owj6!5kM@nWg*;zb3 zR!FN+Dcq&1Qb?B+5sDftR+5}Rm1Nu|mxRIrNr^%$l(a5M4~eR>yW3i`L}=xjl|f#P zDsog*x+GBz>0(b8#>nV~Fn+-8PL}jxRSqY_aIq%=zuS}Vue z3s;j3MIvG-+_SSRV{3a>B|Ra9C8H;)K}ipY;iwdWu?xCzI2@9CI-f|`7(7bC;i#;1 zg)~`})t*brgv%N;$yqMqZR8A)g`p2?ny5!RtVOnVVPv%^s-=z~h0v&_YN#!Tq!4uO z=nji)In-FzQerGCi%}8A4j4uwT6DWrwh}Egc$aL(vd~O4s3tX{i)!>@t88U?Vb;*g zWdUq8)D_mD59rZ$t87`{l|nFNX~vKRKtsNesfubz42hzw>)Sw9Ga9t0kp>O-v-#x( zwCz*MR6~L)^dqG!BEe{yC?`AD29qqjl5D#h7M z4Aq>*z)m?JX;Cp0RirLO)g(32v!$#-lowKgN-a(ZL|6sMsHAsEN+_bKeQ?vzGE&M_ zGjlEEk}!)d&L>VSCl?Fpf-w;nmB3so!h9yfTo~@zTxNlFCWZcoW>T?)7MKsSF!VyH z3nnC(4|^^u6E?@lu&7C50{Xexo=d`FS45A5WU*&cnXowx!|H9$ttXdVen8k zI+;!Fv3!bb!$V7w66wWsA)PD@k_c^LF61a_I?Up&))r-28xdD7s@N5dgu<%6YXiu} ziVedUBIi=alFJ#iWl}!ETC)agBT`sg4}}GL$^Fji#sWH`9+uZRnb!6gx}8cco`V8v zcx?rn7uEG4c7=3J6_rp6$OJ7iD!v^9`GjTmuv!;_Q7IzrS_^VL7C93es0>+2NRnjs zR12;lDFXBFuI38yvbvOIs7ukfu^~;cSS=b1$1qto;f7GWzc7VYuNN((>M&t8VqlBH ztF{77QS`9X-2gS&UaZO01>jaNtjYBiHLgqbS{Pa3EWhoCn$Ls zL&chdkRpj;N$T~0ijkrUxKA_7Wp0Kh7MC+MhZPyMeswPgI*N5I!U$^WC}ERbD%329 z3^!kz+~)=j#kv-u^O+j(KIaoaE2}DWg&`LxIaDkP^dhXp+#tG;RwKhOqiaKTPNsby z3MMJ z|5t_k_(uQVgp2$S@+$vR{_Fmq2{U{@f2IGP3tOjCE{Xs}fFeKw*=eJg8;^)?=|2RVG)1Mkh?Ln8SN2jN64jwA<2%%yyu=l_Lq+V~~=W*dH1 zg9!6@%Qbd|E&Vp1#az_-40{hrs5ZQ=WgAFY3@>hG);HPhLDqO?j@Qg+$-N*5>loh5 zW#h&6p2}hux1ZTTV6H!HZzo0?d+F@f=2P}=Qv+v!rn$%+zuD z#v~*eTcO1*%_c#(nh}Hp%o+;@-xV^#eS(1g-NDqCm3i$sdpF@Mfq+oESGdFPvu`FD zBlI19zkLg-42-+*WrpWA9Duq&^f=fY=%ZwMcNr~ha9xACtO?iH?vGR1}6{4rsN;DcR(Kk(n;KlXRD zUCK%kpa@U|C;}7#iU37`B0v$K2v7tl0{`a_sCP7P!mC%bOMn)yO^uG0&15U0v_H`( zI9ggPaSe@**4E9)k9J&@uS0gv;? z?3kc5f2*Te!d8|R>ZNTKU$djRy*v#{;w{CZauqieJS~pqj#6pO?GVoIXl^dWyVp3H zTk$Vrf5O$|*wo4vPnH?ix{Z$Zt$=~~Sl<$MVufg@2Jav6j^-A;oq)Fw?2V4*E%>Lo zt6{5mv~1$=o=WjRgdf^s6Q5#)p9xcEQX4p8qEQQU0y` zo%}8Q8DOM;6ak6=MSvne5ugZA1SkR&0g3=cfFeKL%`oCdKSsY&fuN6RUySbJxuKzdi5Z`UaTi5>$ew2TC z+6Et3DPaq%j#)H$A+5bE4NBAGDaN9d;`)Cr2eBO}2K_VF|2{XwHy7gt7yN2Pzl`;N zW1WN9)LOCrZ*@AD_N|sRf8G-IIgrBQ`rmJdUoGetUjH}P;O7?f(_H`8vry_yW$Sy!*hS*e!_i?`vj+QZ{#*{ z&*AF0dH1ilA@`&1PrC01L+KwyfFeKS*jsi@&;>(_K_%S7K4I=-OM(WN-}_1>aiN*hPxl*Rc|OVBKt_Bp~{Htt#`0n z*?OeRh>2l!YYcVxUVSf?TvJkqBlnQVk~$o@8>?GG)Zv(2>m01a3RoS!b1&ZLU%J7; zYOG)=ElcVlNrvjOq-cwS?Pdj(L0Nocql1mGyqS%4-4Mq0%v-avChH{Gnw>Q{)aqb$ zmM>*#P17(4{_<>9+yecT*(oHtG8-8;On;er3AY;`QCqdF5jQ*7J*=;&zbd_(q!%-) zO7Fs0`EZU^i9rmow>)d;#XHwKScUagW(p~tB&9MtNVx=;*jttnM0emUJU9!~!;4F> z9axda$ikYkoun99SW_;>S$I$u*64ODz?HHv*K^x&ZE}`O%(Sf}&616o)`r_LJccD1 znF(8PcXXGs3g9l_wq&rG#8}i`#5>qcth=ae6Yg>@tio7VZp01CWoR)XTS=s$$cWs4 zwY!imBW68TSBKRZE7NsYvd&OvM7EGfL!A-17OSg6>Wr9X46zfdLt}*Tv4kcJh|^GM zNoXVqhFVKP10L?3NVO%l9^>c0>d_PNHP`@$p%=vnB*svPVt9<718G7({dgd?V-jYh0UX=!gbxF7Y`_WM0^r!a zX#MZ-U%?3d{B8a}LV*5J1SkR&0g3=cfFeKZ6ZzvwSqp8GG;r=NZabkZ$FloJGU20##clh0C(h_6;-?QDmgW|7a^c*> z+@Q8JD2^Q)nHxW-hQkxt&agarXzJ?NF5u*nNjpS%l=B-Leq_A6xyA41l+87b@0}Pu zx9{xI^k66%+BP>5nmsEXo|=#|hjQw~K>xzYp=o_^c2*wiPfiY2#XYoiZY5%=;*o7fs#>D zC?G?1t5H>h>Xx9o=Tq6-d}<%2B)oTaIhRYG$-?Jk7U#`+)`FrOkmabVMiq5mGB=lA zOy$l;SZuY?NLy<{d3EGyEIbz(9GIQVZ=0K)%nTH|mEo+qFgrCSpEz`~pzHnOq1@?H z=Q{hxFOwH%CWa<+x%9kgHEyIDRCT0F(x8#5|AdTXz-@14(Qc2!Wp89**TwF!uV=~ThK;kY=g95> zdj8J}e}wb@|BH%7O{EA>1SkR&0g3=cfFeK1B`hHCNFzv-OhUl8TnC`)JH>SG~6)wfJ2h%8~5k&bgraGn}Of^LP zDy9miGNuxuJ`vMyOuH}*BI@0VX(y(aVA_GGX9uR+F})bmc0@V&jvmmhn6_cM1yOeZ z)6JM(gy|+kT^liN#dHIv>k+M6hiMC@YcXv`)Y*h-Bc=_Q)+6dzgQU7vS)`5r}5u4r4ape5}ZyDjY|MliWHBba70u%v?07ZZzKoOt_ zPy{Ff6ak6=MSvpkKL-I1YiC*yrc-C&Ly+kCKQG+F!1Mjz2#*P06+SOKAiPC*Jv{yY zlkijFQQ;%PJOAfcPA#DbPy{Ff6ak6=MSvne5ugZA1SkR&0gAv=5`j7g$FduFd0-w2VW*Qe7V$(FFRfMvZD@PhQJ#jXxZk#mwr3G1Z+--;Gt^p7Gy5ugZA1SkR&0g3=c zfFeKb_6OPXQhISNbvC(NIG$L}r*et;Ty}XWPr~WBL?|iA zsk!h>B|}r+u{j0GPz2=EwF#QnyoFcqYalJJ=fN8e#d54O9F~_02i-H&Ti2_4*=0s|?06?_`3aPUNtkI0lrIvDSfz!#{?D1r7hb)R6R#}K#oKGbd zQu%yx9`q9hhFPGb5)jjLWcK91;K1a-c;CQOsjOB3ROzU`1O13^5@RwvJ{%v8jgB5M zA_w{*mtxB>wHJZ0=1^+o7p&KS?pP){-xkO(Br_R6KCrx)PtPx==1MpO`i2JjM!?uI z{@uF*Vn@gM%UWD5*%*8NR7F?BHZP@e3+a3w>L;Ht!>G+l$7W`HT z*1Iy}3YtkPUReyGWM+q&N5}a+4X&1x(DCvWg{c@<@K02Vh^lDftT>xkUQC}dvm)$` zMggYBhYw7X!3GA1O0LoXQCn$cDTr$8Z~^V3C<-Nr*1KBfBkcL4Spt%37PYb}!s@Ix z8K?w@(D%<87fOY!Hm7R9!Muj` zPcV{ocrRuqS~hQHpS`slvJx1c6+l`6o%3g!-7V+27<*l}*$2@CpmWGDKD|MhE<2V?Ypo^Djf_~oESMqq7DQ1y=z;xY+=uzv;ulQyw=q+ z3v=#~ipsBSL#1|@jA*IkoMgEXu4G-mQhTUmtkPN8Fe(MC4Y}j|nnvs0c4ez9DZh|A z_thrTqI{)sxH06*O*$dKAYzZO*KIZ7tZI5@k1{I}^)GRDPLZ`4(HudlMAxi5dQ|aV zP&SrARVhG4Bat=7jq48kTrIH}dqa=epm2p%)|y!}l_~xjJ+-C^qm5Vhi~o)(t*q8+ ztT3wPHB-PS#}oC{30qxDXD53E1DijUNf%OyeCpJ4YH>DI@zY)gVMXGO#pFV2CrEaj zKf=0Ny1Uuyl31s)m|95B=aOj2ldp=fmFX?6MNNSxRU^rzCCmCq)5_LI$kvrpK{S(2 z!MdwzS=K?G|NjNP|5vfLr#~qI6ak6=MSvne5ugZA1SkR&0g3=cfFeK<`*+vpr-#;`^5GTSAij$9$9N_a=O|cn>k2A2M!KdfT&^ zceJ#(!){%Hdn2p19Oy1?f-fa=g+zWC?>Xe~4@_W_ZcY}Hk(r26#nEYOn4p6Iu$@?y zo~YD7w%)4v(8h|v3VZV#`dls3VAd|YpNQiTnT2Q5^V!@vq!c~AM~|?QQY58?^(vmW z8iqZA4vT44PQx^kVOs}aHrCS9!`{$|_mptPCCqZ^+0;xfl{~pZ^p5iVr4_iI)GpJh z<>X=^T{vfM&8_?x+T_L0pkXI|Hk&WNE>mhY4G*zP&#loGJ!%uM6I#6qY2ENEF9?Vj zMR=kuzw`B~3vbUan7gTX^VNLDA(TUACOj#W%D3Aj&2m^FKpswYC|X_1zAY?++n%wt z2)SnQ?}V9uLNddfC8e%qVDqX%c#D_FprH5q4%vmDe{Uz>-=>rZc9IvZj_9i3}+uj~%Nv0#eZFiqo*0T4^8JJWZT{3aq)43Yp@f zmz!1AG&;_2+YS%bJIfxdoBP`d>zNcZD#^!GJ(N<((;}0PFCUX|MVqNBYOy@6Fu76d zugDe#&Z&!CE$2Gf>jd*bj44u;!EAOeg-V?erR0pFYf1ANj44%T7+vGht;uwOsxP=C zP$xy>;G95vy9+%SxPdi0ikWoTsZ3%4>OPauLo;*AoHBzezJ`FKX64sl zTwOD%_ZngdnUYQDDLpm|8uIW~SBs*s*F|uZ*O06cRFjb{LDUhZ{rp-ZHs%W$e$;IVkFgP#g(Ckr~ ze=4}fCdOAzoSqyt6D!Xc&ZY9Rx%3ixq-SBW&h5~$;6&PdCY4x1w`eu*bY_zI6pUjl z-UrQ|Ov2m<=lqHVsK{VpIa5e4Wk7lH^ySJoU?y+z1`JNirWckME=ah@T9nPe(FEi8 z=1dxFES=EAb$ekqZ;17R$Eo7|?i1e_&Sn>ul8fg6SA4y&96DmV>d^iF3b$amREU(; z15#B~JFIiQiyr+NlSUOoRm8R$-NKs5lj;`b!*yA|Fm5-YWnNJ81yhU_NU3?@gS}OE z;Wcc2qPuW-k^Eg~w7tIF972$qWTB9pJ-z_>qF;C^XGU#Z0WXu|Ng-wxFLpG78@Ut3 zmi(ieG>p+qld&d7r?7D*=QsAkDio&EidCp-VQFz2fkkLY3rA|ew8enQx-V^M7W|Q(`-V7J?!`5ugZA1SkR&0g3=cfFeK3&-f4U2lzdFgje_= zzn$O0xAFqd@pk`T{lEAB%KsDpWBzaZzwZB{|FizP{4WaXNoWzA{CoJf^Kar`$KS)hf`2i8D}NJzp1+1)=Cgd7pXIOQ5Ah@Z7y57U z-{^m)|C~QBTq~RrP6;Q4l#mdP2vhv0_>b~GT&3_di6TG|pa@U|C;}7#iU37`B0v$K z2>f#qaN1e6VFA+&rYA8yfoU4k(z;r*Ry6bPuMxG2MmfrI_|$8pSk%X&6%-QFjQ@Mh#OHQHO%4UB+Pv zQxVaoZbaQ(mo6@ zx)IY>OgCV<9@BN0wqUvzQFk+y7vOdXioF|}d;e`Njt9r*sg@O$Cc!q0^NfED_q!qOR({O<^KRX3qR+7#Q%W*HvbL&%lzm0&+s4TALQQ;tNr`=xA6Dzui@{8 z_5Mrv=kqu7H}cPd75{1e6rbVe`59RAALgg{aekQZhn?JR-*sNg*7Lu4-^ zdl1=;$Sy=KMWhFjC?XL=!ieaIgb>jXQ4vuPkr9y)5fSM|qzjQCB0CZ3MC1}gIuO}` z$aX|7Mx-5)ZHR0|qz#cRhy)PXjL1cZY(iusBCUvQKx924>kw%{WGy1ih%_P6h)4q> z^@yxNL_mZ`#E*y%5icShL^wp;h`11`L&S-Q0}(qSHamR(-w6%XCOnRs>sjH?!sGn& z_#yx2{fGQ+-}`(ge68Mp^WNgU%*%N`>AA@h=Kh_#f(y8R;J(kDaf`0sy58%0x@$|_ zC+afJKRWMm_B+1dIOEu8|A;+r=WLhTHnXo`&#)t`jrkPw{0r(4VTog6LUSj}ak3~y zf}$1_Ljg&RN};GIcZJkYNR!k;Dqpz#nzrQJ9DIs98o1`#Yp-~9T!B{#M@79WBI`;x6bfP9t9N?9!1d?1 zTNTULLN%%>U6Le+!iqef%I4-%VASmKKpC3L{Znz{xWhpAET?)vm zvJ~!JPG{xz!#SDZK$LTw&NYBqcHo>$OiMYbsHJL(2%xN*lNDy} z@OGr`l-cZvIyovvqN>^@YI0Z%O9W#HFrJ?34aBp{eX()CbvU2Rz-R5}aBLwPK(WcW zm56HS zmceWT4R8>f$>!nf(tyjr*+Om^MwdV`vy8r}yu1)FlZ!2m$N^4US1*hhg1)R-GQ99A zINY{Mxl5JgGRua{)Ogdkgtb$}W ztRkyHtC2Oy=v-y8Dw18lx-85lYMAtsGFc7Du3KGJ($$DgIzpLj2+6jrDjSABMJMg9 zOg4;U*VdHPVB&>p>I#Lz8f1)Xzf4vK*?cpSz0#ci)Sw&=NJdRr4G)%r+#$> zsthK{D->Hs3f8QyK-M);R#s4;AO*te3M2{c($p0cs7L|7ssbGb$B3$}pg;o!V}7il zMx8*_N4j955H)>e1<=6C>SWBfk~%pmiCr+Hs=B;#fvVJbS5ha!9Wom2l(4!oz*Xuz zHPtCmEgY4#u8^*ZVr1nCuTsaYq%MSR|H7iA>(a^%uu|O_H&S|%O0fbM#;i_rhT zX##RY1h>4o?DG8aKpNuVtNN+4=(5Ik+J!P+UNK`>D990cEQ_2i{Uh`3twZ?^lyzVk z6&U{4#4bgYpuK2Qh3xD}G)~5I$=Q?O2BS2mX&t=1*SV?=IUEj2YG`F0A*92xx(;0l zX(4@O9XirsUtI?bMG+~yvW_s)VOw2?s_Akhva*f{=$K)Vj`ioPjR<2ZDp460iE2pF zWGR~gZ&f*HfGey&EZ(w;E4)Qmiyu1zBqjNQ#@V|W;akG}!gGWfAu9OzNBMX1FXMCY zoV|;0@c+{PIse<>jD6g{)%UpXOTIVzZty+Lx69Y${fYPE-hc5v$9v2>=-wq0aZCV3b?yhopx$WMSjK7? zX<;5G%!ZX^%!ZjJbNd)DZDr}FZ) z?Su68V1u=KDofNhNO-IzKs;8PHx2;L=Isro%0LVytJ?aphI(uT@@)q0oJw{Wzc20s zo(`$ru*AY;pj+hYC47c87CyrsBPI|7zO9}0$RZ09vdQ?_wijEq#;^*z7_wM}e_y-@ z=eDL~Rf(%)72(@W_)1ol_)1pcn7}S<)f!?|iHTT+e|BFA98!;ft;(Z`v)C3(w&B=7 zFQX&cthl}cc4l9$SWlmyIpZ7YcAG0sv7GZ6_H<){>EBVv}4l#3Z< zsSxYl0%B4RS1e7r3@T5Bz{UUwY~JoJm8evX2~>{Fkh-OvBM3ior(=xVS42)nPNumbOC3# zZZqsL6A-Uqlo`3Rj&K{6nF)y7Fwcy<*a_Snk`38t#v)c^s2Q`(;bge&5;hhdtwKx7 zohz9N5%6FMVs_Ypt5Y)!welHenw&PwX&7ncG)ydUwz0t3-i-{jvLW+IJm~yCEBuZ@ z|Noy_TGPT(1SkR&0g3=cfFeKC&;N>9HOzlV4FALGBpf06$*|6%^!{G0h#^SAT2@i*{S^Gp0O{tAAQAL4iODt`$d z;Mej#-sb;<|L6W6_`d;f8+_dVe*ZiDZ}z{||4RQ${Lk~>;D5Tm;LrHy{8zxKgHeCK z|5Cr|-|64x-{^1h`}_{yUwpsy{nYmZ-?!jA!oT@G=6k>Iop3JUwZ2#SUgCQmoKJYV zui(r0=6qN9;=WN|zwc6?>f7nt=G*B0o%?UTCZEq|_x{QIEAM}JzwP~s_p{!QxvzD< z&;3#NXWd_Qf7ksJ_f75>y6<$q&iz*J1K#`LjKizEw|k%OeYW>nZ^3)gJL5g#o$&7S z?)GZlo!+h94c>aM+w-5E$2~vu{J`@~&lfzO@_g9y9?!peUgvqG=f$3zJ?A}VJz38& z&y}9IXT;O%iFicM#h#6x29L*M9zOy^6b?yN$b% zyM{Z(&2v|BQ```@i<7zS+(xc}bGZlIIrjng74CU=k6U!NLnYEbiU37`B0v$K2s}vy zJPw=9#{7J$moZHHn71?c8liib=NqA0m{)@u*0!&}+(80)=1vkg#oR&y zOUz42Aj`a%1QwZhlfVM=E)vKv?<9ef%sWWn1am(Lq?xyqz;WhnBrwmsl?0A4|4ITW z<}D;J$Gn*YW|=pUzzp**B#>m@NCHn|?jwN&^9B;Qig`T=T*CSO9GcOuOWe> z%&SP?2y+h!9A@q&fkVt)Byf;#z(vwEu zB`1u)i_=EnMaPZ63+9c$^N$&U+fq1({=v)0bzm4@cXhVl>&{p&x$fOZu6r&e*WEF4 zy>u73MhD0>(nGG{K62G{at#fUt2Rika+qAjesT>;&5+Z6Z0L`V}!m9FHoc5dlU00>o!7p>MDb zBlLCn5FL^I8f!B`Uu791^cChmjnJ2wzZ#)0F@G^aUu6DlgucN1(FpxJ^SBZEJo7sv z^f2=qBlJ1umqzHb%rA`4zcD{ILZ4xNW`sV?{L~12ius8V`XuvXBlHR8N6?H}+ol9- zCjp81EeQmfKafBN^CuFxnE4e6v@^fPSZ>e0m z9s+2hc=n7DxG`x2KKe8xaDBoETz8cbc-ECzjqRCNkn1xpC)aBalIzo_$@Q8zxn4a* zuIEO{^=XI6HF1bs$H&Q4o+Q_n{p7m#0J%1ek!#}wxvn`%uKpwBYImabf4y)!Bm7DD zt?+Z$0r(!g3Gfxz1NgM?G2w%-3vfSt`S10@J+KdO`;)w-pkfpOiU37`B0v$K2v7tl z0u%v?07ZZzKoOt_)Iy-n9;ma!9`f5S!k3${Gl50yOke>!6Ubm^0w=LEffLx7KpHy} zIEtMKOk!sODt0CyV`l=b*cHHf>_%W6b}Zn>ZUp>(wEo{BT+X0x{|Ubmek%MBb^soM zQvhESJ|}z%_5eO4{6FDca2DWAunX`i;g!P6g%=68!ij+Eg=Yv)6D}|L07%s;2#uo% zPy{Ff6ak6=MSvne5ugZA1SkR&0gAv=9s!3PzJ-7Y_8Mr%AM(eJ0r2<#H=wWYqf~Gc z0N+$V1l$BL$U^{woC7e(IRJzF0x-xg0E7GjFvu?egZu(8$Rz-S909=F`-p%K00#L0 zV2}?02KfMBkPiR``2b*$4*&-F0AP?000#L0V2}?022O}THL${Cs4^}Q9)lDAFBYB- z{`>}oox%ozgLD1g;~(bV$G?$(DSthm`_?!S{UMX*he|4`=S1y?^yS=KZwyt=^Y=pXr_R4tpi_ciWC z_vP+BcelIM?R5Rh^)1&YUGH?g%5}5roGb3qU9EMG*L|bz19fkyyQ}Vbb=TCT>Za;q zb-}upI=k~1&aXK??0lQ^F6Yh8W#^pppmVQtr?VA~zx>AWh~pEEe{tLjz6-8&^f^?= zW=Ea|+WC2{?F zhvK(QCSti<@?1haE6d_!LR3YW`AUmJ^0SkPPXF`9V|hM&YEqqTOy&?}7%k0#*j)fk~$HaK(yq%=0L5A;eR zC{PsT5b!698jR9O?Yt$1nBQ(dAZg_gJguk}S23|mA|NC-I5cc9Lai)?dHs4wLdIAC zB|=vjFyR!E^~DAv5<w|Kl z|EOV|*d@wRvCFRm)M<*1vZ!|VGp(t{AqAPwh&U|{KBjUGBq!))$$+=W6 zA)Qsk#Dvr(>kA9a*Qyfba4GTYSl>ka;FNw)oRX#|cfz-|nOjNhEu`y{f z#{3(J9qyCDgK8)&1_!z);>`2w9a|yqzF0&NL;ZczlgwAwKny!RJpr|^jmD*YGVV#^ z(}OZNXgGW@1X0YRSlRgCs4PuQNTPmN>Qnle&-xv$e%p9|T#jfO^O7}=E%2*PpE?R3 z-qsE?k2FE)*|8}tBuS#w8xxhFCT_q(#n{B*p`#G3#X{4}gCuIKcTm#B13^7F$h<)S zUF_JHb~JKO0t0m1%*TfNG))A*4_(TUF)bLMWS+Ganm>!;;d{3@z7NGm8=()eV|^15 zT@+(+DEeqz#KnTRkf=&1YJZ%$4HpaIBvFZj9}E!~dzmj2&fc&#IVH*A_>qZm=3BV! zkHrqdcZKzVSZttk5hICWpa~8P40JLtZFU49*9i2jaIk-JY*2~s>mCVXe8->yG*tx8 zD01E!A%;}BAV5R2jmc-V!05!#6!X^NZ-xAR+eSx^pB+so(@Nsda;|VZp~DxX!-@tS zTRGASjR_K^>B=-oJiN^@?6={xgX#Qn70^20i+Ei3)#iQRKKLcSS|)9nGbGrjNqIh5jXmH|43qN^e8xmk;j1LP#iRD z!-XA<_knvEabWcDz`|Mp$p#T2Xn{jxQ{kZArRdDda1Te(&{T#F_6<*I8C(sc6Iw*n z#|96h+GW035eFZ7XTH(o*wSnp9qS9h=lqAq#>V$g^fC`LIyR$7_}Vt~hXEY-aa`M@ zV^Cr8fw7^(-8l9|xUGy1^d}BYL=?5Jw|lt%nyR>c{a52Ai1Xlih3GUkJR!46Nw!-^Pg#Z7s5YCpI+kox*3Pg;J9@cRTFdxj^c(D&-~ zsrckX?;vwGMrn8=E^3i+NgG#U@v)f1yt)((agsQ$k?5Q7NHyFy7L?)B>EU&jpCM7+ zh`Zs?xtUygE)bjO4NUa)#`fV{htj!hK9%SnQ~HBbgUpv&VK%T0O!Wogx%3kFoSA|T zm!8D@%&YJ`h|>C!OUcqzp2j#DjgMryVH!MYzFNCJ+}qn{b2gOT=U2akw3sPUwoN1f>Y{ zfP>8YiC|)4|L75vIGE@Ubtyw3m`x8Z;aR7Tq{%u;lfprnVXasyN#=D%{_;2^;2Q{R z*W>{_eS?6!Xk;T3izk`etCEbuOfoMlYLk)GaF=0ZGhvCxHsg40GEh|2C8N@m@`uvA zqC5%D)gcTG^Bvqn`_NE`Z;=qrhoLMVGIA-#7tPv_ClAbzvoHW_G}-a3KoP9%#rW`q zX%f_9i-EkPO1KWSqcMgyU2x^Kr9$fZvEUzJ#|v8N|~az$c0pO_!C_L#$bI`eajC zf<;d~?(ThKL;VVP*$Tubr|^8&Hvk{OSK!$rFdh0x{w~ig_NMl6sFG z4y%2VcnIIVe9VZ8gFla61#Y2WLG%fH2hn>#0}nl-*xxr2-;HOP-q8U?o`9iEoEn!# zr}4Pf8;fh|6uKFk#Ci5=XdXKVbFwn1ghb{pY;r7qP?QfrQx6UeGB3vidMqx7hhQS? z4eDZint1`vGuA&n2KOFv-{hgA&%|KI`lrB~nm9ahXflll&R9Rp*wR33a_lJcV%&6M zy<I5V6PmEGtb`ai1}Gak)%UG2|V{0iQ(XgbTk-OLKwDKuzxBbMgp8>PfMYQ7!<+#9XJUHimGxgp1Kr>RTOZea1gf{ zNu3HzWZ^A-GU1@gJyS|RGg2`T7NQ+1++L99|N9x?FT(GIUkg8jHvqmbJSu!$_>%B2 zyaVtMd|6eJ*OxO?K1h`qa0Zss%6>>rb_6wdS9L4KhN+<#p z0g3=cfFeK z;LgC&)M6^PGXM`jn!yFYrcGR5?i{!P%jAn!QUR{*+u8Nt&Y&>}4*{mf(zCfNaxP%x z&+UUx|0^fCcxnMYUk|>q!4>jUYB3Kc*NB20h}dEheMmmJI2S;!3?aY3{^bIA=k5kS z0n#q+U^Y`o&ZofIV$u8ig8hAg>i8CL9I$yaH@rBvJc}SI1-65?fUP|o_zPUjgL6b= ziW$T?kL{lt9Ej&PfXje-g&Xc025%1YU_Cf*o&^`+%jrVC1sn!6cW`Kb7yboe`Fv_& zCIf!Lw}O9w%^EwH%!3CA0CQ?KmpX&$VFoNm*HmHFxf$F7Z0TeNiteHF*k4D<9dzEg z89V`O2{L1;+2c+-%R1qqKSv(=2ezPyfFIF~UQ8XB+7ad2Fue#^tG9J;&l>18_WmLni|`bTWWrHy{GX12}XjfJ27@ICLI>L+1fFbQFL?rvNx~3V=hW z065l$p7_Jz1BQjO2yhg@Zf7{;|8FmIKO=ks*7bA37XB;z^Y~u>U;PjG&-hi}4}JIe z4*Kf74}0(Qp74e}fA+lJb1gitujjta-OJ5!-QeNxUEtwQaXs$(fa@mLxT~S=D|L6& zT~*iSe9U>D^Q5!e@oUHZj%yuz9A5k9>@TuU+t=B?X1mjNm933^jJ=ON$#yfp00S=g zuLrz!we-ZWV>j4mi4dP};5TCdcIKhh6Eo-FNek?nXh9KN)q}syP*j!SAtE^a7I%Xi zua;O0`=^5aCMA(eEo4uph!-p5H6^jUoSw7DOJY=sL=||d9MW`fZwv0dntNi%`3s1v zi6pq{Odxg)giFE_a-|ENOjXEOQ{Z7;4|oH! z2uoGMD(qVsrN+YGXcYXG>XI0er5^AMwyLmFA*@Kq6Fqpmg&nylcnC9#tW{+U?nKKn zmH{rzbtiWSGi!iuHRZ)>uc$OH}dcbWM2p20le1XE?5k0Jn zyTNl zZ>VT^{Hn=%54aSwWNfW0Vs&j{8JaJ0j|{>cCA?LmV=d=0<;F_jog5tOh9i<5z7#x- zwd^X%D5e8O(=TC#v>KJdU8*XDbV(7RsKH_-$=GjH0$c)uL(`H_(P6fvbxArrqE?XS zF|%fg(8@Jy_`a5-qS6I^%|p7_1D?kKBcmI__+j>86^;~$;bKn$fc1bZBbU9Z-lfAP zd?XYFw_|2!u=c{$WWlk$7z+1*<1tId*7mGQdO`|IMo)qV$$9|%XiE_oyPyjrzqdW$ zhHQ0VWAG>mgBxU}E2PP)toDFIGK+9oVQbp$DdMlDrCZ8;=`pmRsTQ&~y29BM3UDKVCn#i$5l2MnVTExO$*TZtALyh}D? zS>$LKstLTYi)s}7mznuimKSCXy<8T+RzqE3coHq^QE+5t%9izA2_9)GnlWSnR*)}b zs-jwgCpx06>)_X{Su+|moOwZ`4)W-fUtUPgWJ--1p0a?T3jIjwibycpDJpn3Ywj)z zLf0ccyb_Dqf+?klxHUnqWP&+y}H)~N!O*vF^8Us7QX|NU*Ls3QQQdCV+BR$~W z%+esr3kk=}#NvcNgjJA?N_v+B4#E)4g8M>LBC)|nLgADT(wG=|Y6!F-s7p%+Tv!(4>4{K9!782-(kWEEE76oROLqqR-#F~f~m9$J!=NWe*ch@49uOD<;$ z;MIw^uB}E2#6vnbsTRRQTMO8W+=o7qvB<%^9+tsjTWfm^-A*MJ&w-4FU6R45Tq~E5 z4iC_j5IAsa4O(PWd^-m63CrwZ1qM#{s1a!wICN|6vB;U&KxN4Am{*d_o@&80B*DQd zc^CM3D-$oPOBoLGfyuS3hLbZPm@L5w95jUD{e>yKdc9~NaC;vSVHE|y7KK-B1)8G3 zDW7ie3fJ6TtjW~{;8rlK$>0^PtR_~KQQK`xO=<_j01NP*@XQ3qi~+B{WM1e1WN2x+?jgA`~eT6hE&q|H|r^}vLH z^_)&-(sOu8o&cw4iBv9^%_SDVNg*s+YeEF~ZF)$H!D6*}xTq`yYlu_J>0Ao;6T^{U z9>!3y<{$)ztiqDi3(MN(k)jH?PczGf0fJ?9dNHxMoT)jipksxyx))ZwrMebj1T}S( zu*ohJY8FI>n=ei7L+j#VUBjsgi-y!fGM#yXx|UTHy222wp_>mCivqm}n_>FDyO7F; zvobJH4#7gZeIE=5C4Y=)fR&4DG$THNd@^FCwb~y{iy>7Uh81}W*fh70UIZ0N$^RX$ zXLN@NNA`|olJkk#6OzY^pMrj`;Qwnomt@WL1F~34Hs( zSW#55YBz=>craLWgO5KLD@Zq9eTso0Th&z^T>Z79QM(MfRda&H1{^>|&;RdWKhD6{ z{~v+p{cjas0nhcb!V&n&e^6L2u>6ns&+_l(@8zGv=in>;gS?DS|NqGUIsbe8cl)n{ zuk;`E@A7Z=3%)XMejmQD|6cEFyf=H7;oJFR zUftW~^}*Njf8hBX>?gd@bF1f!XVx?1+38u!{h9kd_b~S!?v>oLxh3u>7vVN@Zuf88 zkGMbSzTbV9`#J86`>1;WzIfl{`kU)Vt}nUX=X#CnR#(FRN{yL@%Otovf!d+J_S z_nf*+-J!bOb%8pM^S91NoDVu*>%7gGb6(-x;|w^xj^D$%{|`A{<+#al!m-~Gg;W1d z`_JuPwSU-tpZykl&VB`)`tP!@xBc1nL)*i)ciZlQGye;4=09Y+2tL>Q0|4#6;ct`E z#%VU@*8@}i`^VwPjf4)v?5`a=)fW$r_cQly1R2G~{94nyxZ@_Xevkm|HCkC;cEi6M6y z>b@yQLO~_0X*yANhY|k`6tAMwO(gympooR+zuq_)pMtyCa8Q(jqroZWb|c|yBte0r zG9=;UhNiC~O_Cl7hl!?_HG)Z7Y|K}V=rSBO#jw28D8!d5jyaRmmo$OIRvYuBp-GsA z43QVFg~;tT=8FgqSPe=<`-_a|FDMekz_t*Heql3kx7(P1pBk8in;D%bet}{8$GOrW zTyoy_`A`$AWMdxUY8*zs4GP9W&L88-4^-U>S+kJGN4fZAF}OFD;&Adk%skJC{s>og zIP?}H`a!PjocYb`K?zjYhqZ> z9HDv+0K@{MALJ}YpKmhwKfqNTYkIa(y!Uf)STG(8g+p-28qS~(zygxF(XikF0Ht_b znV@omQA=+}rfDH!+JMqOf?Mx(M(P{JEQjR#hmF+ppp^sbUr*|#OC0H&B$e67 z%xfp$^_6{u{h6r7+gau{6H+K5%Ovs{7eQo%WnNu*8Sp4DYXCeal8J zx2rmsq^P797L3@Jhg4|9AuWP=^8hdldB4o2h1FpB+4PCQ9BRv*Eb~$u>`(^HBmePJ zsBr+kFR{Uyf3On22(bjFvJ%U@80)f}tP4)+Sp&!y+2E*Su;M8CLOlp>Vwo2@;I0^T z>&T*^>IF7U3kAy$_hk%K&$nrMBsfw2p2tZ8uG`AqcOaDsAJW04xz$neB1sycVWDL_ zuK~_p!kQ{EnL6F495>LtWpZc?PW(x+Xidn4>@1U1-yz_D}C(>MV0`gk9_LJ^4{9Pfo& zI;>{KXvo(g0SWf4!cYTABl0|ngatB0KFv`5EKP@j7jB%e>O`xPh4%JL>+#6oz zCQK97B{c#KANJ=6Yn-rVCnQ;hH*HAWPpt*5o$R9ZMHaANay>*#?1JSzjjiw+0;C)W zK*}CAQ}$-Y*7)QYB;yiIkjziwISsayNnhSiVowxfNfnQi*ffe&bQxZWL9mW(fGiaD zIG&l|(DPs%-UAe|^3fJZ3bONPU<<*1`N$9%*hU&5zMVZbp~8mn2plX0HJ1_I6yk+_ zOtHv2Ow`Yr>fwbwqJD_jGpoT+08dUtGMwxq^*1v)4(|n_*&yBz0}jb}kSI$cWoQE( zZZXD0qHKWJ^t1`s!G+ARP5mT;1Rj`W(HNHdNCsD-4A2Q_$N*k!BU*YJpk!OwD<|NI zgrOuxTG$mRim2U7)LxF%!mERDSUl5k75*)JyKG;q` zE$eVGmej(fq`4eIv9RGu+E5Q+JxEw3)V@GLl(0_Yy8|sm`ay&k4r6!kWxCiGgW)+$ z_@;_SyRl9BI>^17oivX)8~0No!hHZ~g_aCm46@QlBurmnbR$AKu1c(e%7N~z6sM0v zgmpjaFe)r*u)IQ8$1yAP45C#gv12$^g3a0gpS|yZkE1x-pFP#R+hVyOSA3SMWXZl> z++|6YW$8{@&WzQdDCZ4sFXLVb6 z0ZVz-4KRP32eW)!SWEcMb$I8o!uJ^!x|olOe5eIZg>MnKs71WYHS{xZ+w8mp7V@FB z8YF0P**BW31)D01c|R`TC#<4hlgk#n9nI$wU$GI2cJbTGJicrBMl(#u@c}iLw+iNr zL00h|%;LWlcY=)#bzHB958GQqJUX489y6<#!`HIw+>Mf`k6ObQeE-i3TGOdnF-3@-QubVGpOX=xe1uT0?k=hJ?8 z%EtFAy!YnepmtdCWgnW6!$p}jWc>uvFD#);lzBmOupqh%#ww6tKfJ=s7Rj1noz3Fh zR8sAzt~Bt=oK2T$Gl59Fg?irfnfNRLfjId1%;tNi^1WsYzj%@FolK7@izD_>%;n9V zM1r!T1r-#V$9HDfT`;U*dY*1Zsr>wmnDF6oi^5lV2TzFKvo}XgLTm)6^6lVF=@RA=YOC?S^qaC3As$%oBl!C!)g7gzfP4> zDpMvW@6eqq{9ee#KY0%Rv76X0Mt<<7^BpPA6wMU|W`)`U!CsmXjV&G0@<%987YLfH zHjCYsaLiFqIgk}j%dE8u14VQq%~98NGL|$cEi(+edlGlc$FSXLnZ@&kfjP9BijJ?6 z#OpCRP0j$k<|ZCoQczh0Um3;{Z(-3EV^-oJzB~i%i=UTeEcXu2l4V@PPYDIGvIlYp zENPi1EEfhmiA}?~byOV~T>YSVPAC@N`K@N3BX(sY^DQ6VvT<_NjVz5fa-7`Q-q;D$ zUh~nkm-HT>y%w;B_64o(amrqHbnP8k+UK@6c0yM6uGRrlTITZQ!Woe$E|-ndj&XLc zk2SA4A1sHAS>xnF0YP6HL#QPLmNVY+YNNNNtjxHvvc!{LS!*mUsx{`P>zc6FL`l5nTko!>HRV$6A z?CgQb0~69RtFnawH~pRh&AztANL_4ktuENxjk&Jd}BUYhF!;C?mAn2y#=N_>c?o*npo>&&yF#2;3Fg^ig-CkZ0un6tn8vt zDn9+j8DwMCWgcVa%AMw>v`nj2*e#B{XU2sz##t+37dHA{89ym@0~oqh9L;@_x?=EV z2%lvMH!wZ|pr1gC)elzmxCApaq(|O3hvYQWf{{0?F|I4t>d(0qW)DmW!hL6P{Js-g z>yEE;53XIq_=Z(jP#FkXM`TGKJ}^cc9jgF7Dn?|)=Eis73&ze(fwauUi-p~O>dW{c zT-AX!s}Y-N!~1Z=(XYDy@CViuX-jz44eMSwOe=u3z;3T06u<(5u;$l3(w~ag4jqNDt+W23WZ_medsV~Swi#S zhYtg2oOh{EXPv(bZUy0B#~3eEopq#U4~p`m57tzcCDfEChit}luf{%U23!N)w9F8E zctmLQz?~^NsGda|aGU7tsvrEAs5jYzKAXRR9*K#$jj@3-{8^ZoW3Y^I%j36^#B*>! z#hQUn!Gtrj@G|D6WzL^3oL)==H}Sm0^oHR>Hu2Dx8m~hydhyrnqkbubONd|1(49a2 z>`A;4P%ea{y9LAs&`^hu9C?X?`$}tuUSL;b)+`kURt^myY&hs{1sfwBpqDfF4pHwj zyMr(fFnDqCJXx_GO8koboH7!IN@C8~c;PQCS=qS$pQ8RAfv+O#b^B0%b^i7^4b&Ika~ZPE?W)zTs9LJ0>)>KfhpU=#j{zP# z|3@dTrX`yHe^67@{QqnI|26;rU()|SxwR#)#|fT`*@uI!i!E^<#~*MH`QW10>@4wY zB(M4J(Y|4(RP5a$XW8VMI_@m{9oi38%j=3aRBha{76SoTO7q!k+<88@zqfikU>Ojo zt6pEtULpVC=oBCK)&^T!BRzEBzHm6w7N|4Z?dH;TWot{> z-!%XKn*aY;pa9MPzf<%74?%(;%7EtopJF)tf9C%mJ|;u~{r{7Le9iyAPV@gCckvGa zXU6yc4+p#HMF}GYU|@il2gE*hyI#4SHK+cC$xq1F$$R7$d7Ydi%hG4k8`AHkpGbFu zHU9eJEo;pSOByEZE zDtIe=RJmL^Tj^D{DW!@l?OyctLtqdQo~FCkwQHnglcnXcEvQ z@Rdn`!hAvWOpLinLAGLZZV;;UefsK#-4MzW!&91ojrSGBRQy3PYG*g~lMTW2(TsvsKUTD!+wQN-Su1z`zw8^QDkeBc3g7)#qC_246P z3C6K}JuA6L)>SukM516iWaQ!1@X>Abaks>M>{SXhG)5NoSDM$*;>l7JTrHmLd@Y_V zkM^s@lZE)IT0B{Z*Q>>ojmMLvdFd_H`4H6F=2_nlp(QOQ_6v$?S5vjNtiX|HsVZ== z(@ikHy1=2Bj$+mEv~keMeG=u0?S+R`A3cbwx2*MoqgGpG4TKziisIHKcxf<# zn}*f&fQ6lCFF)kCh+Rej_^LNnfp>DJwXmwVa!ukM3+`FM_mph{*Sh8s2n<+hhj)Th zUA2-t3!cIy=-IJw6MLICCDHxWqcpES%rg$#MFIY*;R?ZtV_|`}(oE5*;}7!g17LyB?yDhr=Cau{)?=N4kGl zO8)<(WTt+iexSYuS^$3mKY+hcf1&;aV*fp;eqX&){RTw-JFH%&9#qd)&r)}(C#ya1 zCElV2)jIGKSfzT@Vl`h~uG-b5YPLF8ouy`~8LB}|QU0ZTth}eZp}eI0S$R(RweoZ2 z3HV;WU-_gXusdAyRSJ|TsC?_dhN<;}Me&qyZvr++H^F_*P#jRMCJY}IW zN135aQB*}#k{~w0-{p7Y*W|y-&&$7&pMsf-N8ro;F8Les_27~4VtJo@7WgGRNshu- zeS=&lZ;>nH4e(9BLbl6Gz*C_SzUU{)202;!RC-@}9lpGO4}J@ulpd4rgYWs6Q z;@`51iGRZ`B7TM)B>pu!K>RejkoZ?@Kk+Zw1;oE#`-mTB=M(>cok#o_+e`c?JD2zo zb`J5w>}=wP*jdC6vNMSvV0(z~XJ-)K$4)1{7yO6e<=(+|65q%Mh;Lx0r6%dX74WG< z*PcRj&B;Vp?cj70(Utu~SM(8G)YhCGN( zWr&TXhz%PMgC&T8^@#pr#QJrJz9PhJg@|ftw!9s3bA%2;+7SN zo0lVQT83ETMyz%rRyh$X9f%cn#EqK?ZHVPoM6U(WV@519A(k#h+^__3-D1T2MTlz_ zBCcM5xN1J)ig}33=OQkn;C=2nn06TvowE@g6wc2+3)8llh?ePy=4pr~3iG#=!u%~c z2KVGmL0mi;F`EMaEt-hwg&BwoCLqpN5a;C}W>GM~ITEI4Q%J$tdQ8vEMw}`lPR>P~ zl!ut15~dXy@ z2IQ#oRiipXJqG3jBsERdDW54H!HmG05LMts<&VmF)kIN6q_rNT{?eb0XHDIxD5zG^uE$^03 zk$dG1h+@znZXWSy9jp@4iKJ( z5#UcnI0fNkgp&~Z5&95%5V{e%5TXd32+atM2n`59gaAT4f)8OELLI`12qz#Mk8m8q zR)kuFEeM+tHX+m?R3lU&R3cO$Y(ywW@FI8+$`DEsHXxKBtVbwDScg!AP>4`~uofX7 zVGY7+gjEPD5mq29M_7j7MsOiG5gZ711RH`C!Gd5$Fd-~OSb~s;kc+SwAqOEFVG+VY zgarum5#}MxMaV*!gJ49Mjc_c&EQFZ|(-Ed2Ohw206YV}0Dr7Lraqv4 zPyIHm{NJEntsYVjz`FmL>P~fs+6}Az&1yhBQQZP-{-tW6x)QANm#T}@IqGzE60G&7 zDF0OcuDlKF{4ap!|I^A(VU7Pm<$Iv_e+#VdU#=Wf&I7Ig)094?LupY0UqI*o-?Zn~ zKdt}i7&iT{=pWZVsQ;e+4*f0qYxS4w59-g;@6n&8@00!l)&akkekT18?ESwleOtN} z>;tXTVZ$NA0mFHQGYvbzlR~$l-Oy|Z7)~^7F;wa;hEhYJ zVWq)gSZY{gm}8i3m}D>*lJuYG-_yUQ|BL=P{dRpw@6*@nEA%D$e7zewQTwM!K$Czb z0Zjs$1T+a~68K6bKuX9T2y<~cv|Grc%~ypvF*e3{c8rgW@vIonjPZ;ZPsd^kg=w6o za?0d%45ulaCUcs^X(Fc#P7^q(oD`&$g3L*xnr@G&f!`ie13w&713%1m(ecAEb@0P6 zb@0P6b@0Pn9sDp?2S3c!!4GqF@WWgk{4iGsznZIqpT#w>&-CN^Zg&r1H(?iHl(3VK zR*1W4g}9qmh`VWpxSQ6MyJ?-cyA-dYn0<%S-JI^?bSI~8bGn1mw>W*1)9swT!RaIj74w9pZE;Ct9g5W*76V zi#Q$Rbb!-^oc43NfYUxsw5VUqXi>kI(V~7aqecB$bCZt9D!X`>L5;hP92}y&!umsa9NoPBk^w@Jrk3E<4*mH~UXl@~40U_zJ z=aL?KF6pu7k{)|5>9OaM9(yk7vFDN=doJm*=aL?KF6pu7I;kAeW6vc$_FU3n&m|4^ zTnimF6PgG~lRSs?*mFpaJ%@C^b4U+7XEEl9^;QetQn-x95<4dk*Qh=a7DT4(Ye&%%J?F<(@-Y?m498op5^Z}>$Ilaf} zT~6fOt^`#hOnBjim;Nff^Z{YIiZ)(Ls&+bg6sdXx|^v#1fBn0mHvtqf2!P21s10OI;!0=k`8bYL*~Eg6VYzc1W9o?R>~jioLTmc&vQ3VE^A0U?*40M-XQVGwe7LN8>;GF>FtVyT0|!dPm*umBtM zg80T*>;I3!9Kd(M^8Xg~INlX zdw=x)cZ@ng)vL*%0q~LXuJW4lSFrT|o$@Qt0r(-<`QM}5rQEK+0_^;+Aw7Wem9vyx z%E?L(*!Z{TuTz3bol*-n{vM@R$yb(xh5u3|TbZlO0{i|9#h|2sb^qVNyZ@{5U*tc? z&&WTQe*~WW?~(76ZyVG9-!{2cu9VBA}NA4%_k@BQIx z{C7&`zWg&GB7!LC44;4}|2GVOGyKu; zTf;95KQ=sKxW{m(;a0=7hRX~W8qP87(y!Eq^ul`Q`t@r=$f zAj-lUIHa_HnglcnXcEvQph-ZJfF^-q5}+0D4~4nJZ^qW}--xZ@zb^1K{MQ7&hW}TA zui?KW@HPCG1-^#=ionCNFXgz-^U(a_4d_Diqf`N|yNzfDjQ4oor7t)FUAfyrhUPvW= zPDmksR!AoPogfhZR?rduM&R!6o?-u@?O(Ich@WPk690;QLi|hiG4U_hhr~~@4~T!x z-Xs1Qdz<*D>@DIa*_*^aVQ&!sn7vN?1bdD6N9J1TEkUn><(V+uHrfgS8<($E4kXjmE2R^72TBOvh75NxMstl zcG|i$LUajNaJZz6wk{46UDQf+u!ZPAGtq@1qWxfM57qADKIzWqKIzU2Qrpg9pfCUj z_jC}Q!F|)6&VAE8%6-%As-t5&Pb4~xmOf9VrO#7n>GNb-;@m+?oF~x|XFn}*_Lbpk zYd0es{U{?1gHA^J2HUwBKszIyf(RpJf;O&55oUMed992TDOwmQQZzGCqzExmq-bKK zNYTi?i8&kC?MOjJiWC8M8*cg8tw{Ck79<}dMT%|gCR|b7wgvaq3AA`!$8N-Zb(?YD ziHvkBPGF>4aXcg4isKmRR%~UYR8h-Fn_>$iZHmo|v?(?*(x#|kq(4#3JsnhW{fSCO z`V$q56el(^(wHb`q%q-T=V9euFIMhhdvU{4iW_BIU!s&NN^D?f;h_@JkXT0=4@IQ$ zkWYFHYe?5&HR&s?BAtd+q(iWR^ca?t_P{dIfp9O!a$+9wNE5D3Jyz2F=0s_X3Aq9VWvFNg~%h_ zgWLe_Sxnj*i%BnH@f6&Xx6^{9&EHRH1Hs+CP#9UI+m`h3#S<5iz98&z4Ly8tgQr<98Ph zL**^yW$^m{EPVGrsr*2B0KEHsOSu`o`7Z;{etT7;lBO_e3;5}K7d-j@Vel({$b)}B zMCEH$6geoLC~uZGLR7xB@^aZG=gA8|e}Af+A&U@^?_==r_lzXLxBs2eDrvIeDZ_*C zE&f~3wSO0U2c9esKvaP+%qg6p)WD2Fk+Krz6Y`Y#;M+eWZG$<5DG;mgB!fxOPcCqVu;yrd9Pn^!K zjb*up?|7FTrX3wH5rMVzLmU9)Oo#)3JcV7sGrq$v=ZUu=5&$0QV~6;Tx7ejT@g~Fq zz#|@ZG2d~BUBnZYu!B5t1v|hKSF#Iv;$pU+Cmv=O@WexGA5T2U&gY2-*m*p0KikU_ z_px(%;&FBkPyB$L%@dEYvv}fBb|z0e!uIgQyD)eS^dL88h4QM;e0qMj(?Bigu)sJxEIdm@qN z1fsIziB7ELZMv*F##Njzs^omIg7blmoG&cryx+@tpNI4LWt`6|<-E6q^SK*hyq@!( zniv;R_3H|W^4AcpSw*yZCDE!CL@SmPEnh~o%uVEW5xJa1P6v_0PGq+c*{nnsu!M(B zHk*h{ONo{)A%b}YJeHSBlv_+xdK}T>)kHb#h_V+GEy^cam`$``5z+hwMDyko&7DV- zW#KfJXigT9aSqYUSwu5t5>1~$G;KQ3)M-SSlZlR*L^Ne0(c}!GNfU@Bs+<&}jD?(J zq6rd_Y9LbdL^ApR+b?~F@&7(l-vOQfmq6?P59)8#U#UM+p8y{K55g?K-Rig0Tfqy! zVfAvD1=tT30%t-Tz*E#dm$u1+M__LPWrqmA@#@gI2)P5EJmnFhB5+aPsoqT_sidv?~rc=ErF}#OXUmYz49J;0OAC8$q_ju`{fg0 zo}of61xFlSD>Ag41>ko5>`8ieF_=b7vfIeaLkpX&3wd?u&g)Zq68ZN7S|&u@3z0;YPq zx!&wB1?GPZH`7=h8fRfTDlrY_9_uHIqQv+1)vRUh0E{nw( zu+=*(7CY?pTa)vre@+q8>~6Ee=X5!(E}z@xwl&oI%}&2HXf>Ps0h7yOO;)39E%jI8 zb;!IGwmgeV|M}N3nHr&+6EA^*B-lR&i*V~REg()8^k~o>u-WPxT=fB`&k+ckO{QSL zVX|8DCmp#6hyEC!+hVu*Kn}K`-)8pveGa!7`pD*X2Vt=nW>Ib>yoK;)LRiR${jhWo zcmv_}gx3*XOLz_8VZy5kuOhsX@Cw4q2`?i&M0hFTC4?6fUPO41@Brb3g!>6EAcQqv zXvz76u%-`bSj-1Jm+&0Ivk74(ANHR~xQFlz!kvTzgb)aTVITm8fdCi=0${=p1c(N} zFd6{EXaG#;MSze13_}7i3<-s}049VGnh+p} z0K*^x41)+T3?je;KLSJ&U>He&VI%>Dkpvh<5?~lffMFy7hVcU!h7({IHGpB%0ESTm z7)A|X7&U-l)BuK20~kgPU>G%kVblPIQ3Du84PY2GfML`ChEW3;Mh##XHGpB%0EV#w z7{&@<7%PBbtN@0w0vN^$U>GZaVXOd#u>u&z3Sby3fMKivhOq(|#tL80ES@#7{&!) z7#DyEGY}v+0K?z_3-DbvFgJR7SQ~H}!AbvZ2AZxgt~^D(Nc;^@O%q)|msv zO$~u9-d2CBJ5;;b+p%@Cy``i%653ka-c%nBbeDy_-SusieZI{lUA0>_nS2HI!F>Kc zySLs_*;-$;p{dpqZLJSigvtswG*G^EZdfJQ)VIab)a17`LY*5z<%NE;x46W%HSDo^ z!rs=c&7NLQ@w$+w-*594dz)(eTP(HJYn#2*t)Z>ejXmYXo5Ce+rj_vtd#lN4u`aXN zmf7q}EKZx*?A*CrCFxm1-HnYVbDys@($W@baha_VmopS?b#8BTcZaw4d#b%n<(rFo zYr`J9w|ZT3Z8NqQ+ELxw;_2ViR9oF*DX*?Ic?xSy-on=At>KcMt%c)C&-67&uf@~6 zE?i!n-|s0bv3Q$ndOX$Z!dr_sz{v21ykSpId10*$dcMUIuCQ&b_S?OM`TdE~vzUx# z+cJxNnaQ-oVRAa$_MOXRl3qiXIoR9Z>2Gc6>2L23)OR#?`ock2+S%S>ve}(Ip5_u; zt)*k7n7!4u#UG;bi{r%p49D6V-H`kbJt3BqC(lakJIhR?? zOUy2l#cbWVTq5c9yWQ>9kR{}|^>np%M0&TkbobkOOyTZ6Q?M6gSzcJt3pGLCG=lUh zp@x>;t?-M5G1=m6-rBOYxTx1_fmRfjz^|hQf8o8}@qfAfUxW1Ex3IO<+q@1WRvGf* zU8uh`Wu?JeeLeOqI1Pax!P(e8WpJhQ`Su`ILMmf7t~+)k6* zZQ8k9Bdd$n)DK+XLT=uX9f(uoy*fn zdhG#6kEN%{(i7>o^*6%r*4@!&k6J_Z&7GG1aBHI`y+rB3GuLc3xm-J!r;+rwyW7Iy z?Jf3>XviM)MS3jT>$@Tma|>t{^!n``VeP2_(z6@kkzqD1v%8mAoi@7#?t3ZxzUK;s z9f6Ln{$NjcZ`9Fh_L&NVmrX6T7I>m>_P`qq zJn_R7@b(PPs_J!3@J3*Q=e^xiys7yJ?|Waz8;i+HZ&8hQc%GWKHaE4__E(tUc?)fC z?Dg~qTD{fq4!e0n3%ob{!NJmH$&Vb5Hb(PUj_hgUxL60^hQw!#~WK+@~BZ4X)d zdV{W({+^~*r{CV#XO1@awl(^Cnr%&u+u@A`-j^!yea{NgvU>5|F1#V^?QgMrEHyT7 zbD#ykJK&wrvURh^TpO;jm5=|%@-;|rLvwi{yq7kwZSq!^n6_3|Sj#up*tb^K^m;aX zdOVgQdu@L$d?)#F+XmNQ+Pb;cIwU<%%W)XtXKS-8vs;!p9iXV>ME`%u>MQ8~?*sKM z^_9WD+raLB*x#4<4Af51B%nz^lYk}xO#+$(Gzn-D&?KNqK$Czb0Zjs$1U_E^4YOuI zrfoUX0k`H%1FX&^mxejy-f-4b*t2%lWc39$b=D-niL)lE>RYfO0#2Nj4k*n^15C|H zMW=t=dc<5^a*`mDYetq6JsF&{soBx~TcKHgqN4`KlMLu1gE6)U5e??A{ne?&ruJoGpqVxyp8R_TJkEDme zL*ShtZS9{X0Zjs$1inHE%&D0OhOaDc1}W;W>>tIoLriWxyL?U+Wf5YQl8^;_6(#1l ztS75PD;qbF3YMImV;U| zyp)66(^8ocu6x);^(K^(wQ+PAiO18~f%dG*(Rqf9 zYh+uNCFEh}XO)dEB{4@jJHMrLLo5%NFqeV}vrZIPf?S5mb9#fpK&P=M($QkfGj_Fg zc69`ekp^RXN2I5Bhvh(pHc_x#2O2T<1ZJ}t$*J`ZyMg2`!DHzlC8tWsz4ro!>*VY9F!ck~Xz*ygB zEDg0a)^|jrO~DRq(}v=q3Y}1)0CM9M7ObtZnDRD4m7s96u_z^>!nIwY)~L~LjIij-10j%V-^) zBk^)=GmVzfITj7J!>YH=Fq%i_NEE7Ny3sT`N5U^(Z=PmcIyy(pS{}R`xb>!~#wDY3 z%pGipTfa2Zm^V7dklnvqzvP%Xxs+p=P$#WTC?s#noW-NF2+t*C$(}SPdvq3Ye?pdp z899q$S-7_b5#yOn>Kn>3m~!eD%vm_PxQU+*=5XsXQjH5nW`W|e6SB--IG=I|@C)Sr z&dCLT26w@q!CmlYu$%a?8#$4`{S5AJKZE<*&*1*{GcYs(dQB&{`x)$NzD3^mGr0Ht z4DNkDgL~i4;IRc#8QAf|JqPUg0l|(R5bXE?!Hypg?Dzq}jvtWX4y5x41?lWetRbCy z+)wBJ_u-MuckbhKfqV%|q4@UjFp;ktOz)MyHJCm}zHcyn7TkT~`N7y9ssyJ2fMDzo z2*&<^VC*lVvA>AM{vsOti)idGqOre-#{ME2`-^DoFQT!(h{paR8vBcA>@T9Rzlg^E zA{zUPXzVYdvA>AM{vsOti)idGqOre-#{ME2`-^DoFQT!(h{paR8vBcA>@T9Rzlg^E zA{zUPXzVYdvA>AM{vsOti)idG3gn(Xy%N7y(<=zc=Y2YP#!sg}_URPJKAi&DrgLznVj(_&%pf2(+R!5%;MTKSh?SnGO@92p1e8D!ve~x%6Ry)qmg#SvgR|gEq3R~ze zhFVXcZmp~`77Sk27pxs6;Okn_(U}(^(b0wX1nUQH0weAT)ERZw((aam!i{)q*pZ~L z*?t^jd{{tdgh9M!+m~wy^Z`HpNIfvr0lhl-W%=91!K!1OAAplvg7mW{4 zYtHz&Oiq(CV72l_#EMulM*Sm8kDr%iEceEm7|W7nT*R9k3S?!cw(8b?eoMRR2U}fl z0;4jUzoD+HEwr63C0494)~Fh9Nkz?Y*^F#k=Xm8Exv{y%tb{YM?ZO#pne*ohrx!tSs^5SJoO!i)xMeHPz)M zUMR#<sSO! zV%dYO86mjQSNm_&oIPOLm64X|UnmUthek2Byff-qxVsZ#LP?d|btg^u?N) z_}h;ks~dUdI891yhdO>cuvnCtFJJ+O!SxNa9j|NKk=ilx%yHTg%a)bBbN((REpy>Q zVYfK+ON!S-x#Amjv1${4OYvi|p*nha9qGqJrH}a&{5J+=LSK(=O=7veu4ohW+R^>Y zvgNr+_O#3zczCQFK5+a#xPe4E`f!NC!ydb>-e-0P?Jg%>AwoRQym7iNete8FMs#32 zXKZ})3bV2q(_IWVb7);~oYn<`4Zg0{XkBXrihx&DYNyj#Z?^jVquQDEdF?z>X-8=B z7^h*|h38PPj9T8gy%nC7bnwY%%hZip#B&({&{T^eie z@Hey(nTEtUtU-0d3Lf_&;;qVpAvNrLc*gYZEKkc^yjVDWA^kW<)H`CcV)zep#L+MH zvmAOAi@)uCk=M52S2Oe@iN7$8^FzdbIl7+-NNiAgM=m}=7)Mt$J{bOxmU+T*VZcL! ziGIn0-#i^5f3UtI=xZ5#zY5r#esGGw-+V^r%NsuMkG8agN5beb@k*#5eA$Ke=A~u! zEf;pGN7deV*VfrhmWH4!U>Z=gj@5kyE^`Lr|de(pERJV3jnglcnXcEvQph-ZJfF=P=0-6Lg31||~ zB%n#)-;scUt4&STCruN$TGJ#oX|9lPE7hG+P+dpGd;GxITkUpEN^^DH!1Q|Lf_8lGOLq zX@-*w9fpu$n_;uTYbY|TFjx&a>g$F%hN%YCkgESo|Gxe;{R{fv>7Ua7Q2nd^0sYna;&+7yFDsi`dgSc0pFJ7c~iC5{DiZ|&Ohbo^*Zs_;>Y6i;@jd&;z?qM7!tRsPs#U5uZx>SuUI6m5Upa4I7gf+s$#18MEYmx z@29_({zCfi(w|EIVfq8(mwMNorIL!~8{o z+Mrgb8&spB%7yAg`7Y^2)gaGQla+LNh4PvFo;+FkP<~Q+UwKPeEC-aA)g|i1^3%$n zm1E>GCo=7uN8EO<2tnR$&!SScH{4 zVHQ^Kgh^OV3Ac*- z_8Cu@*{3{VVxLgL_K8Hdw`BR*!{F^V)xOuo!v{@W_Ay;g?*p)yV&=Lo$R~BZuXtn@w@H#x zyEAtD+r%z*2eFfVi`c=wNo;4g6WiD~VtgC1mEB5gVYkHiW@0nD3E6ICHzGSM>;~Gl zu^fwdlU+;Oc6JT2!^93FTW#!WWV4-JMcXcRC9>Vku0VE}+2yqDV3*NxCp$!J zXO|+|-0TuMZetfCJM8QtI__WxiLLAavdzpcB(}2s#5Q&T9e1;R$POzz9~t`PJUZ@V zdy(O~&ZTWTJBPL{>}=Y0v9oB~&dx-J>)C@0*K-CkT+iur+y&~;xZlNgA=^xBC+#=0 z0or!3(^8W%1iRx@B&+ij+OnOD>YJ4$2^ z6WLn%u@)jrGm$w&WNJdPyBmpI4Mff$kt0B4_Y>LbiL5>%%Qhl&9g*ooB%AvLBG>Um z&f|z2TZ!zoM7AwN*3Crb8X{9QlGR;Bh%DJe=0!xNg;d%C`u^ANW@GyQdrW;$ zy;Z$IJ*-}?UaW3YH^J9*nOdx_RadGm)e2wB+3GxXwmMy%qE1i^YN{$IpDKS>-USbQ zFDrjho>zXSJPkhheysdJc}R^Y_bM6k@$x2lgIp-PWQ&|DFOZG$ba}F@NWYbSDLn~Z z_8yYHA>Ak)mM)VHO6N;wNxP(zr5{fGLu^{?t*)IYEPt^Sw#C-sl(AJX5Wze|6+{wDo3`pfkf>G$c+*6&uI zQ14N{rS4bvg13ZSpd---^9;wSLA6=ETiz(IlGA0K^r`fr^tSXW_zrwtdfafIa<}qL zm?yYaxk9;Exj;DwydR#b^eG)mixL3OhntjgWxbLQUJtEGt}GtA3EroX;C&itA*ThL=5w0IX)dQMPIIVaBjIeqV+p}A8MFZ$lL3NbGC*)l z1~`pyDk1p&hCSc|9B>MHcoQa5dIF(Js1VA85}|<*oPWbvB4IjV8X>p_hy5vp$-Ie4 zw4oyu2;l>W>Da#r|4H~6;irV35dMSkW5SOJ|4#TJ;Rl586TV0IF5x?bZxg;n_$J{S zgs&66M))e>E7U~xGHtv-_!3Hj{eaRB5k5%x0PVk@@IJzO3GX5NKH>KWzf1TXI_qx2 zy9nS{92zv>82)hZvMa$j}QRLob93 zy$~|=LdehyAww^O480IC^g_tc3n4={OJ_xbm3Er%unO#+$(Gzn-D&?KNqK$Czb0Zjs$1T+c! zcS`_18{21_h2johcd!p)$K)H^TYdhZu>o8pMw`HAW@i#)@no9W+SW)%eLX_Rosq(p zRtN=_<4L5*=E zWho6s@obEmL)$$*h-=fQ0^dGU(3#SpBVfiFymH`hUd#{=3Jpc6FKrGzn-D&?KNqK$Czb z0Zjs$1T+a~63`@|N#HA%fNgN1WCE1DfaXM0NX=aDo07D2^o&RbTJsN^>`0%Sls9VD zAQg@J`TRf%TKf;qOpxvW;9LOAD4-L9_#_8Rex#)(O%ZWE0KfnDOKX_=4{441zWOH2 z1H1r!0H0Bxg4uw_)CZ)S)bFX^mJWk``3>sT>LK-jdLC%M?^JiF-D*2%z6aD3!P34G z>;l%Q%TycK1T0ds)S2orU=g4POZ$H+AAvo<>&oAhKY>O4)5_13A1RNhYZQ-CEIk97 z@5>dtlC8`Fz4r{oAb$)t`EP*U`=8~f<)6tvQmW)f=xYI!N6jl zHc9vWB(D2@3fk+#-d$*~5Bwh5>jR(8)!$D+bA8x+m+QZGaQ*jwuK%9N_1~wUsXpX+ z2Tk>X-{uH|N5hWfzw zGSZ4~W268dW~2b$%18meg%seMNdZ1Y3h+&&0N+Ro@C~E@A0!3%04c!xNddl|6ySYb zs0P0+idff)ctQu_@!JuPYfnwm3GAE*ZftEstPLY>X+_-Jg1D&}u``4iZ9=SRM67N= ztO_Dl1`yl*h!ypS5g+2lZHVP{h~5(sJtrWR9glb-Sm8tUmw~}O5*XYAT?7XAKnKC# z9_RoV+yh+*2KPYw!QdWfpNHuDGNSXq{2q4hEg?F01E=*wd%&b0j_oPJ>ehk9kRar* zL0q#6arH{XRVxrzEJs|v3~`wo(d|NXIT4)>M28*GZbP(zWso3Pz$Qol7XXN+rHD(H zATG&6%*#d0Ek-Oo4sr2n#2m0=5`^rO5#Ne~uhBQ98kIDY}+y!nW8=OJc+{Sn|? z#5q}rMz9YOgqgDtXUs&LJ_B*ubi}FC5Hlwu9y1AX%0$G;8Hkg>4oDCts)Pz+#zI0F zae{=X8W0t*1`-5WK8 zGWY-7%~@-(CIL+XnglcnXcEvQph-ZJfF=P=0&xk%4fesW!7zh;+J7VA4TRSdUPpK> z;WdPZ39lx+ittLpD+n(qyo~S=;iZI^5ME4p5#d3?1B4e6?kBu}a35jJE}znS3C|@w zhwyB|vk1>5+(UQ<;ZDK|`&5Ke5Kczefp8K+KSCcuFG3FjO*SMAS=)o*ITPZB z>`Z9K%?JWmxc?8>;^Sv(64~K{3BD+7Kv;(WM(m<63t=Y0G=zx=dIS+0LpJ{pGj6JH$CM4hiV(Nl zXMzbg$Fp+vf9lY2#C_d$QWwTNV8vW~)0%eurR&1b^p99|Pjmiw=7|`pNlr%*r ze((k2bq^G1GOOHtpNZ*sb zE#0Q>Q3oKhUzZwDL#kgrLEQ||2ujt}sv9B^uyiUGN-XTZ9t{?z$3aY{4;Bxr@*cN-o0- zA|rswWCSp2F}7!7K4J!`|I4K2FO!;oDyihBl1hFGspKc?F=rC#EDWhDklq2PI>h|- zFTm8$(*Mw^Kj=U()PYD`{|9Y|L?r;wix^uIz`*tYKl`84|3Av;fLzmFxe+w&uYp*3 zmnau1=P73?yCGU$|CgL;;C=+2gL#IZ%TLIULIl0<%6G`O%Gb+R$(KS5y}j}tc|bl% z?vf)AMb8gj05;1Naw*vUua@1i6?_6Ll;_AZz$<_%i*l0m+5cy^TkUc+31||~B%nz^ zlYk}xO#+$(Gzt87N#Ng^P@r?az;puT|37L0fM1XQ3nmM&8Dt4C^o?AF`zjGeN8cNz z(hs%+Oo;3CGp^T9-`8Kia(_ZZK5XRBXZ{Ec|9|IOKSAps>i{zeCWtcpq7Ce)Oi|>Y zi96GuNY6{VAWcZEO1UW|mHkwAv+ic$BUa2-(GOpECoQvPu`sYQ)D{T#LhJ;8OC4b- z8VuL9M*O~LDAHCJ3e@@Cfk41)uOE@eQBYZwUtMG@@fH?sF=mZ8I%Wx3#&YkdoLR<2 zSqar;WitrLn3j2_eYcR7IeWIS&mHyEw+1nqLmk3k;^bHZs`J;D6&bSz_hW&fK$bBC z@gf?79Y$|?wb5HsR%YB-S>nmBtTmPv)f)3_s>@5fP=cq(Tb*mnYJ->!S;lT(hrh|! zvB+#PWe=VQM+3o5e@CbtB{G!BVjrHVWTSWJ;K&kcD$9lrT1FHf3i}!pYPZ33 zY7U3mLgB73@0y`&>T2r@HMT)Oi@}R4SYK373RgbjgjFkzrfevus|^BMB(`AK_0@NU zS_3HMEMt8n(mGfwoEYu!`CG<5)ZT^C>I{y3w9^MJDaR_YEfNhvQ=-A%XktY$;DU{j zjy@Q2L)S_DR#~*JsItggP*j!B%kk{7-p)1_Vy;RW3nku?>XQ7jvf9|rqC)D!247ce zw63$Oy}dOQ>^O>g;#qNMec^f%$Mn!x#BrE-JqbUX_z6c{lRwhd*%cmJ$Uc8G)ICOb z!Uzxf+Td#9L)~f_J`y6~I)78p-x3Ve)qhT3_`0G^bzPmoj-zgIeM0F0=WfYbzS_)>a>Eh1e>cbKLT68ZsefNPI@a~r#iac^(KvS(!vEZlW!T4o)zuwu*>#)lo762_HTLS~SgeOG^4W)S)j!XAzBb`tLi zxb=j5t#v-Tzrk*?+3MgSw|zt#&9OGtcuOj3#(h~iQi0<(Io{3iY~c;b%06@buD-O) z#fyb~#q`oLqA?>jH^tf(dx05oGCp=jUG^LnVhcx&Tz4&{y4O4m#Gj?s^Tj$n5v z*i(mL!-DWSF!H6OGwO?W!4n|@tpBWV=o`^r{BnTxKLXIyl~-zX=Ig&19Y(7 zQLiT$Y0THwR~O@>elW(L{YQQ&Nf;sV6OOuu_-Gw!d%TAdUq>hx!cmty)Qk8gaOC!X z@fU_sPo$ydk9?D&rpNPtp-X`9YY-f+4!(8*L5dSMy7#-P7sCDQjX0h9b-Q(a*Itp*hLm5VXz8&EUZJf`UP-k8ImN9xb!NK@Q9er@v-FW1u zVtZ4h4em~HK@5BI#r6MW^|ko=|7%Jf|NHNn=+mx7lYk}xO#+$(Gzn-D&?KNqK$Czb z0Zjs$1iqjIgbm4Hh6~314{u$d#{m#*QLyYV0G-p8F0>WeojGo<)-pH^AS$vXBzWio})d~8i{tM z%tBlDtENC9Q+QL?<`sFzETI~jsn3Dj+vh;;>{#yn>Voxk)kRg+c+MgNERqiuHB^ji z@ETlrwMn`~ddkV>3mJmUME&4#hqc$(-VupLqJ8bbq~hXk4+PC{LYY>qP+)7Z!n}gD zRTfj;#>(>Q^6J`+MT_F6o7_XG;$Wl$qzmF2EH5Qj=Ys06iLv6wJ`-&WbUL*92M`5A zGAaS!`u~iipD^`Yb-Hr1;sOu)n;Oax%(N7Yu5*Md`H@zV3=V>RTzMk5b znx1knWnS`~$*Yrol2ogES=XaW6%GpX*qu<+f97A(RFdDs)&J=C7zi z+y7(lO#tJ%s=M(wi;HG}n^hV~iDFv%?{lK_ z=Dm0BJ?EZ#-?{5KN4K*nWA8cy6-m>t5kXZYB5hfRpd{(iH6o}9_RVR7FA%g>ZKawZ z>DQ`+vSM$#^OCU#6r)Le7u6}MVJTcJqAaZXVH97j`DKWz{ov;=8N1h^sY&5*QLL-e zR12@xq$!8(O|2Lj5hBYaw5qltIT(=HENs5#T`Q!*{qMe&F zb{3?%N(lv7Pzog!3k6xJaf+|jE~94eO&L4ZE~65bg-C(q>J$ra)+8wF_A_%v^V(%p zG+DfMWt3HWbIRDhP8k(hx>kBzLBmNK+X@7$f02qLOV_HWQWeCix@2rENOcv8f-Dz{ zC>B1tDlMn*YR!t&RJ-dPmy9iI)0D%qR;vSw%QzhrD)ho&Yg#SW|Z z8LXsPe6==B+0MN2lF_sdO+{9Uy|PsJ5?0bIzFL!}BHCT|TrxJTLsOE~qE0UsW+lzy ztF>v$cH37j8I9}E6lAs7bIGfhv-oOln&`W_E*TAL)0EJLiaK2{G|rXfEWTQkrW~sf#w?HNJ*9T;JS6?=Cn~;Ah@h6&3_h{ zXKGJIJ(SlW8NfgDl2HSS)duhdii%WHNX6cAWi1w8tyzs^0RIaz|F84zW4H^xpZccQ z_p(v%L%6m6SrV`$U`fD|fF%J-0+s|U30M-aB=Eme0!E9m(98r*vPK(Q-{h*Vug8uh z<07WREKB#x$`&5hTy?<07hQEYpUcg4ghGppi@{_nI60rm#1~;-mq<+qafmz^pYI3@ zVIee?%+AfkFNb;t`$EwkQB|}yy3QfCm8i5~k8W-{49^t z=SgfxW{?|CFtC&ZtJ J1+!P{$x9^9-#IK^c;CMM1K>DrR3~%a4t0s(hg&7x}!W?16yO7U0Q_MLFNlu^S%6J9F>yHR}y(yswBb- zu&T&KEaSsj%)!`X%r!H+47AV{4J9<%FRQB5eIl05o=fETPJ9uU`9a#=h6B;_@l+}? z!^@y31=UVo#6saoesrETK|YqAPvv+mFg7|gT!kBk9!UGPDBHw|K;ue|99#=tJIbRED2Z=uq0qfz>>fplmzZp;3Gj*Z7=pv zGgWMhPMF?qO74iNJ?pJBSgnS#A*MxS9%F&*Y(=QO-{ed))O7)hjFAQ%O6YT>bE)r zHd{{$9&HkETa%jRN7Ct8N3F-%%-}KBHqO@|(+1~0mksM3((uU`kL>Dv&OMART3A+t zK?UdB1CnX1f06eNr^~s;=1RcVnuDZwai0WaF4Gu*KjR+dzQcW;`y%%U_eqxZKH{18 zUiB*ON8AzDef9rYKT-Gnx^(To)?P&bgICqGI^XXcb3AN+*dg0h+n?LEFgcRd+W(B* z$LTSslNHCHBwms`1ieF+f}*a41*LH4i8j5*cMK{va0bV!q!?T=oE#Gb0mq<3Vg4xe z$khB~)m0o>C2L%gaDayss#z?{`DJ~|*fLBRjDo?^*$P+zgs1dP&B6HxuK#yn)y3{k!gR1uuqAw`0^qUvg71A^<0MP)$} z2S&TWPi$zkw+H?YLs)t`N>`o2Sak~NoqTw7$SIhhrq!o>$VjU8g{3&(BFUx2kjy}p-O9jUwFUlQ?+93)- zNmL_Zcmu4Bk9P56>3OoM8sPi0X)GQ?Y~(4<7?G#(srXzjk@3sI)P-Jhj46&wfyC|+s zD*vWLX&D{wTsH{yz_?;LA)>MQy9c$BYSCz(MH(in?t{1V| zy%@(H12vtK)aJS9>J&viDCio_{#K({u%&o{6vNV7*J>2C4pk4Tk}QfWgk>-U? zP>e2Ck*eAusZ@{1#iQe*yn6i=UX?0PBGz2A%PSfNVyS#;ekLJBybCw4LswHu)in&rDVER%>2pHRoR6bO}bo}Vx*smke9QdmJCWHWmP#BUp=1k zMw;e~T{lpLG+t6jN&v?b(bdcNK(U)4!IEtwU44IS9EN%@q9XbzHje#|&ZQ*2p2jfSr!Y`FgPd7+tYR3!j3CR4VS5G`GP= zC}Zdo3Ww~AlEdIl4Wjo9Yx)Kx*B_0c$wd@WsS&feVgH!u?SizbwLUb$@~Zbkvm4Da zWieLHq7>!@j8YVQ7{RcniTXxZ%y-d=)Z|Q>)?{6^T7K$Mb^a?M!rfw(kN$Qhhns-V7y<4GC8sjN;^Nw0t&Uig)$MqysW8; z&^bkD7P=xbN}%KTTi_y;zmUF|$hJrV^V{PZ(MxSrvQj!!A*ji+TGEHb%u4!jES>1(-mI)jR9_z%!Q228en16u)>rkwP6o8;~hqCV{Qr!g`+= zwmlXJt;QvkYM2ai6}5^+6~j$;M}n&*l{#b<${0h6D#LbB=kAXLR!ItVB+5ZuRyDPn zY&0ZoUo9zyPGL-1A`wAY`8|rNzai4LT2hQil%NPp&q%eT3X&dPEh)4BPFO^Quv(=Z zh%%D4u9g%A=3)?Y8z{Fb3`>ykq1BQ~Wc;m0R3%d7!xlv(^{41xOT?aUSmmM%`qySGDGGLFf2k?wRyd#XC1sLlz{r~-#;r@gBSMC?w zkGUV_|9cC!>_4De|11eu60js-Nx+hTB>_tUmIN#bSQ4-#U`fD|z}gb%X!0;STUtWY z-JR*tujn%n&K?S`FeK9#AWuRT6kPco3U2Z++gcbiZC9rVR}Gc;av8p3m;3rg57XSz zqQeA)wNcM{r%MS+zm6tmX43rS1a@G~V|f<;Po<~rhe8b=rlqB&iE@h6^k4L7DP8H; z*|TR`?S~F>9tJ6>qrtL0s3Mw3Jt5i;?eP&jW*23v^fsQII!FHOhgw*Qu!mAELx|%& zL4kzFcLEIW065kR(7TQMBC~lb0JjCey&0fp6LIdx{hHbA0^sTa+;spowKWd%hKBv4 zzW?kF2Selk)o>qYF#G=r_g(ID_-XyKBw$Ivl7J-vO9GYzED2Z=uq0qfz>umJLR1MkxXSvrf z+-JFOabJe*|692?a=+m|#C?MM8TSM3FS&PdFXNtz|31C^zdu+(Sd3W`uq0qfz>U+^O5J_0KF`ZzI$3xxH{*$<#Biw8^y&o4v;-k;|A(B$rW}Q0K7QnU~u_ z^m?ByNUtxk1?cr&TRTB>+1kj(V>?VPZd)sT`7)bAuP?Rv>GdAlK|;{uyTEYw`2L^o zMZUk{j`}Wev)os?eca#pzJ!<7KT86Z1S|_tUmIN#bSQ4-#@Ps6= z-x0IT&1TO{FD&S~Ff)68VPPh6_Tub$WiEbpF|N+e&7N;0NOE#vdRh=K$7j+PQ|XIg zQBH^TWNt>kG#y!(y>x{mNayCo#L|`QC(l8E6SoUyKq@ZEFp=PAPJYwPsNjIIWv7J0sk1fmbs!{6mw@& z(@Tp}$;pdug0yf&PbDraB%~!(%%&DoTIQ@IXA-$&L`a|0(rFh#nw!!VrN#5oV)}}5 zWg5gHnUtE7lV>kvr7N>D)Aa=DQY1Ayd+DN@$tBf9JiRDgIy;|Eix-6{b!k$~%+^t! z!pT`}Dl>m2vAD35(`LlDcrH7)G_RjatLK$jsfHjeruEtE<(Y+x3vvc7#}?v?T4s87 zE_HEUNYChDsbQZZ=2}oLCFRRYiSWfMi|1$bNpv1 zZ{VKAo#XnrL!8U^sP7ZphkS2+LKSQwTN1D&U`fD|fF%J-0+s|U30M-aBw$Ivl7JDDY6J1|zxv4}|WjujNP(j3K$J`fG3wN37v+ zeZ^~)2VbJlp~8(iJUL2{x{B8lVnxE2d4~`QMkGzv^qTV5#EB?zI7ytri&{7$%MH66 zoi5Xx0Cj|koE5lfmx8(m-}kzC{{M{(_iyn2_Z{wQ+&|%*|3lmdxWC8Qe_{*pL)ZjN ztoZLZcl?@Esg;Q(0ZRgw1S|_tUmIN#bSQ7XjD}lE=&GY|G^Zb9U zdH%oFJpW&7p8u~k&;Qq&=l^TX^Z&Kx`Tts*3uRB%+Ujws^O$A;Zqp3FZJGhNZGw5Q zKV)8l<|SZWs0o1EGy!lIO#sYyih1#ymxII#q0##&!+n9fpIhR3xMtsvd>`<=!gtmClyO`YrBMR4bt=Tya&)lh%x9AdJkQ-S{B zF+o%ZMPX14bdLrW5V%^90zHEy7x_9kS=%$7s+x%?gkqhiWJywb0`!NX_K-xaJ4vF> z*{X?zNN8Xzs)rFtj-c+Ks58}3!vs}Bpihe0Oi?dZN7Y09qeHqZEm73%6!k)NRILJa z8%0f4M^!_e-J_x?QC>q^fx6}78RF``OzsgOBp@DVKnyBEAUY5jMa;uQHh5uf`rsCl z_mPomd55JCJo6*|9}&W4%7ao3PnAO1WL!}qQx^zoTN6R;k*cB!p~3NCNfgw9KzDGs zyZ7)Wf*jpm4Ovn`#aN&uW3d6aJI9-er++0z#kQJ)A*jE==*X<|5; znux{cf=e^mC4U`~u|qq{l1QYMX-wLQb4yy^q;Qh3PUin=-FF!75uE=wxZAl1=kfi( z_YvRgeDl5$U&v=?zkxM?7qMqqjdgp!?|rxT9&g%v%)8t3ThBjv{=)Mj&y1(vbI{{( zf7AUQ_bc6t?oqekW?hfEKI(dd%Wyr#b;Py3{@3-7)W5C%j`|~Y->G|g-Kjcv?RRV6 zU;C=sh1%n_?X`6^->!LY&8un_Yp$!g#ra$37oC6Yyw{m=4m*QRuj5h2#~g2VJj*fZ z=ydF{|IYpu``_7LZqM0A?QnHy`?2j4w%6F6ZaZTO+qN=~F^^!$hzf`zeWeLb50_!s-RH_ME?2>*cPGj)ma1UXx~Ix87OP-5-IHY)3so`B zmN44pO^lX&S!|>%b*1<2xTAE#=c;69cR#fZBU=??q6{Nb1;gpSy$s`06^wfKZ6%C7 za}>i>Q338-O9(CLiYySN5~Xswndg;I_*4aoS&2^fnKF#oDj0TmG1LdDd8P`6(|xKe zkBe0?PL^R@sDfd4pD4pfR>eRlS}_tJnoZ`F zW0}-PO9=c_l`Nd@kur?Qsu*)+7-y?s)VhzCVZ^IoINj+ojHgz?XmAggVN6uPaJh%d zFrHEc!{;6>!??W)hQl2z!?>*qhTT0-!eAKtBP;a8X7^J{7&VKGH;8y>bH8V z>{~_Fi%4kCeRlsl;ha! z@C4h!NbY*(WtlRh<5iG!AieZb8PaeSq$METTWlgiWZzH~B$CrhifsT$gH@2SK)PqP zj9jb=()B=k@k|-gKoz78Aid~f8Pc&TNGTw_@Inb`;1&}J^>?hEAueC8nEl{`hmYp2g4WP1ASOky@$$2|XhDYw1-xU-SB4U)f->Rm*jt7Yu7c9*jv!RK*_jEE7QG6}W_P%(jqKGZ zN@WEw?{({Y%Cb?bW~1#cLs6=tsJqHgpon6NSOq2EmUa|S zP->wHio-27m!X8Jps;RXdl^cw3d%lrXj>UdpbCoH9o$+%+1pN0D#hE>{m=o5LbT$p;`sfHJ361E`k<`Qx9%@yR>EZNFQS$={(Z$v%}D|IoSVy< zfe<;ox0u_WBHgAti8hK;%4J`{;g%}6%o|PIOd>H;_BpieBDhpdQk_L*SZX+;+gWO^ zV%v39kW2CLfV{IPe6eMxiEL^yXhR}Qx7%52xMItW>L_LO+nTG8EPL;EmYT5GvY8^6 z8XqCa{MMas!Z=|bX=$pCl5fJ~5PI7t6Q!bA*&Z`XMiAWjwoHDvG*-!vyWgGH#1y5W zI!eB^QxvWW%E8aM%Q}U2UlCtrS+@t7CXA(TA);u;cjO=BL*rgT)>_TfNME82D z;56OsZpinulrhgT93oNRa3yK9yG@j$xJct|y1UpAB)895x@mW$khjD+@zcZD@q4@9$(@EEen(!o zL$&z%3;I(fKfh0Z2FTB^(w_?X`5E%lA&{T{K%eU5=ey|7>&ee|(4TwB&o|=FiC3 z+w(a0B550bBR>~@9;QEC9{g;jKR3C7tWe}l%**iGpa1<*{60v3Z(;6nyV?7g&$|E2 z#{Cv606*m(<-W^(gZmPq0e*`6DE9$R3)TbPi4}oA#hSpYu`2M#SQmH}ca^)$Wf3J{ znu~L{a^u_(*Uv?{8#tX4xi)S;w~O1#aa=uT^ZnNMOZT_kUvdAf@5jFH`@ZG-itqEj zPy0UR`;hNFzQ6H3=zELrjlS2y+TbO=7x6`T3<{S47`ucn~`yxKc z*XG;r+vVHp{*_4&}V?XHrf9^lSS%tr5-@*PF z`v&$k>^wSgyCEgc!pY6TsUGiRXzt4NoJLP?f_oR2& zdyBW*d!sk(mAq}<{oY;PtzOPs@3p!A!SnB)UwMAw`M&3yo-cbo=Xu!kkmm!QzxVv5 z=g&QF_B`NumFHg13p~&EJi~Lvll3%tyq+4*tovU)=R6ahQ|<@ZY0q)bEuN_72KE$t z+@pCyoMA+#ivoXf==^M! zaB+Yaf`TTWZ0E(0sGa1a=aX~%NFtle5>8ff!N<~gg^jK}QaqL8$Iin|C*PZdPtja% zF53|bEiNtwr;^jji;2s@$@FXpdvsItleugtk-7=bu!lF5y+Ibm01i?K7iTh)xj<@a zWdXxw1$2x|h{7?mgyMnQ4{T=omy7m*c&x;FGG%fPp{Pkt|7qidq-^|P| z&mPRqALWxNQWD695WvwXK2M;XhdYniPAohh;1yk%g@9KQ)XE5YVguwjg%m?2pBxhe zA-QieGhR*oA*%^;iL9iaSV>4NBZStejO!C5)dY}9(>SwtGt;-6 zR7-4Vw6|xB9}Eo#qDOgIIKhX5YG+9As0=?Y1Ebv+S~fHB<$3!R z{TQDY>yn!yaenYnd2Em4wINUWlwAO!xDI?(bbucI;$zYAcG4tJnSrt6y~Bn2SxH1BL?&cC zp~$^cM1i=WL7HeTy^5rt=pQp8ySFgMStC&TZV4rMINHUZL(9XyG9OK67Sep@d@eng zoJr^KSx(K*B*^!d|NN4v;N*NJ6JJcHrW2`Y@)-u>^BrLnC^VJK&dtOxhk6G4LeU;k z6@@mMUt2N1HZ#AK+?#4|4u|X}v-agofI(ww7nF{p*$6;PuKN0V7PfR?K2pq@-wRge z=5vX?{48a9S(&o*2qM{Hk0&#enfSS!$>Q(5Y!cO{6S3oheEPzPAj(K;W_cM+!DO!6 z%v@gH@b>h_#(6E!xs=TE@yW@=Od^w>P2>_8KEzL7&ZOJrOckC-l%Sq!PJ2IkdxpzRp4uG|3jpmCG@k z023FficX|LXjcu;EoOHR2Fm&~!a!SBoR$(@6pmsfvh-L6 z^3A3*kGs64TD)brj}VKEsv6ufiCvw798d-0y5`MHf}JgW3pP+aFyA$kUgG%=n?MNzL~kovZEE1vL|9Tg4uK~iQ^juX;fPw zD{^r#kCBU)`6anDFux!dC-ZZ1aWg+77YFlG^YRmN*~$EvT=p|RGA}2{m=H*eT%*(sX z%L~b??K1>!OA@&C1c6(|$?GQOMdsx$^J0)!O>GqJA&(lEPnegFo0o^o%SX-2ht12& z&C3f&vW77VM=2bkP^XZFD&v^X5L}LVHhIQrS6fw(MoTLY{e-yg=cNm^jwD!t}RF;dK=5q;Lm?%@l4YnbtFJGcOOAm;24j z^9e@%0)>4Pc2jr*h3yp50C9E9J4lK;=I!KJt%Kaah~2z=)4Y7vygXuF-e+FkZC>7J zUS4ZnUSVGDGcSKkvUFxByhPy~g=q?pQ#efF5QT#j#wZ-1@EC=+P#B=FmBK?5`YGH; zAy45p3b#_Yg~H7gHc{9}*m5x6FfadPUOsJJ9yTv;GB0m1FRwQ*&m*ZF=P8_~@EnB+ z3a2QXr0^_-aSESG;RJ*53eQk@J%vXoJV@aI3R@`LL}3Gk9ECm#Y0I)- zCco{A6!uftL*eck2Ts*}%IR>~ItkcCfW5|ngQu^e@Re>Z$b8nbVB`Ls`!)A-?nm7B z5eeXH+!wI|@G$o=?!(;s;0f@Z+&j3paBt!s;QlYx0Pf*lz}?L~le-hE068wrUEmVj zQ?U+kf;-L)Abvm>RszDDjCFuk_JiCWZU?u8Yv4RwE!G46!}qVgUtlfZ2flyyecksZ z-{-Iz@Nrlhyx;dO-(O=r;H|zl!}8$OzWcBu@Iv2neShS;>brt9fjQqr-#K3#s{$us zl@RkC^+kO*Vr4*qg~DOq0Uz(%>D%gS^m%=CSR42q`y2L`>`&N#!Ro*_*e|i4Wk1D! zlzkWbPWJ8WpR%uKU(LQ8UJahdK8wA|US_jc8<=L}?5*rLJH+;5jo=1WXGOM+J;3f^ zo7pCoW$Rd*_usw0!YaYPc>me^HSZU^pYeXe`;p=*!JEAgcwgmxsrN)7VOZb83+Y^=3{yY6!7;(i6pFzL5Y%2bH zn)4fH4sK)oRi^k%#bo(^=6wGt2evQ=R?P89$%@mzCpF*C?cc%#R!s7l<+Ji@Jn6r% z4^3vpEFVo~`4s&c%=>TMyM<|4G0kU6Q}rh^;lJ3jh4HVL=c9Vcrsvme(m%^XYAYuC zklOMo`n8$*Pwz3CUgb=mDW9WXn_0inv3n~cubk>L%V(XKS^vS!{*A=0ZA_%fTz>`k z=A8L|>Iy*ix-HC}6_b6YI4}Rb%=!&x=N4w?is?R+pNjui%=&XXwlHlg=KIXD`T4b; z{u{fR@$s#g@H6F;?*Goo|DD^>8dl8s^Q{3h^56H=KeY|2aK)6Lj5*5Y>SPB(yf%vf zb6dAC^2G8b2GU{a;}bA0)+WsT26aSPu~1E3msX(&UVGJrSE*v*HM0d2C#z5th5UMK zfg<|mqwu?AB5V%M=1OZh5tz5rzw1x!cS57NeVwf;m0Zb7=;f} z_)!Y~fx?ea_+bh^MBxW1`~ZdTr|^9gVs!x1n)gunZVKN;;e!;ugTl8{_%;gPOyQd- zd?ST#pz!q+zK+5ND7>G-*HZWz3SUiOehNi@zkd*0tN|)5imf&F#>KOpr3#~0(uGPA)uRpn+dp)fEx(7o`53+bPy0BKqElMs!R?2 zrIK5P0EqyR0D*uI0YL%+1hf;-M!;bLS_wErfS-Va1RNk>KLPs)*h@eQ0XzYF2-r;k z=I)H`Is$eQu!DeR0=5&djexBLY$0GX0ZjyKBA}6g1_Eev@sVFF0e03i&up)~%HZVx z^SEJdi|@`|8WK7w6u#r=po;<~T?KkFyzzF(KF{ny&7_>YCfeyj8S z&N0Ws_JR6Eb^tNbpM7dCG-B}lP4HzKGFG;U)HCLEyHfYU=$46mocbzC~{C# zRZWo#?37q3JXw{&mGdmH_;{vTepzE4GHQk>Uk9oPP9`BA5HCVkBLyy=0AHm!8TeUq zTIQG4L&nxY%16&~`5>@_LLyVha#)FI8{ng2`bI?axVLTnveI<7x6$!nXa7!DqpQ5y zM6p8_5i=twi@GYxqoh6M;ujOhe=tjJ_!~d=akYf%gjUGM*D+=MlCJzC0-23l3uN8NvfIm_}o-Uowbv_%7tWlKFjAa z@l+PZ+hnJ1F^-*Ep7?r^)aJS9>J&vih^RJ_tX89_=9}^pq!^awx>lozP&j%}m2hOH zD#Z~O@l|e4X~K+q7$jfnKV5K6X1K6a+&!l zavEi39`VhS^Rs;Eb>TRdna(eX#2$EGr^vdv!pN5xDO1kG4#j9uA=`c(%H+sCDDC_x zi`YZ+g3tkFB7k{WQx&0eiqI@{MP!sfhoiU5XLJ08^uOk84CPwN=Rq(bW*T z4QEbD`mmT;X{a!kPG97+ICg|(o=h%_EFX$U$Pxu84&4g{kP$RZ3r51Kq*NIJ5pE*l zYDqD8Lcs(DvEo+NiWp&jn-q|=4_|AUOsLpo5{@mX3$mt&)jR8aZyif!1G&p{5ZdDT zL<)6;(G)Dtsogn$X&z^Z;6Y9d+a8OAR^w9cki%qX3~`(txg7RT388dRVE~nrn_19pD@kL1qPn|=DiO0 z8Q1$<2kT!`Ut5>pa=wRsHSFoyzkz?gqt16bLyosN4%%O3Z?rw1`6ZH=|E)9EFTFNu z&r-ciCagcrdhKN{7Afa+JJ-A3I6WumnT z6DkRa8P?GZt0;+enHcTwge%VOTLNQZps$CBBfrp4Jnk(@ohB!mUllhS^-rOkpm!*A zLeB&Ml4mKb1cvU$F-=G(VoH4 zp%K1wuybGtAGRVy1EQox3r$tgn+?|)%1mG-Gw76Lgm%^C@CKVI#;!_NcXUNtEo&o6 zCl)UREdar2l5zcM!pmJvRd~U&CO#WU(8E z4{KBWaD2+IsGJcvMMaqf`xVL=lOT;Ook{uz>oqz76Pv<$BvFI;hfa!tl^j99DPhwP z3D2w9Apvux1(09CLZ8uglJc8g&aXhi_9|jf)i5Hd(p#=8ZfsGvb}tpW8@*$M*SjjGW|UVM>%-X5LKJ&xBV+!GbHKD8{e7AK`W4KB zjeVn3=o2f2E@7If2W3^(t1VMJu3>>Nn78{ZAH}44`9nfbobPSrD~)7{0d>(f7Xuu6 z49G8I&TY6xD3@enQyOehaf?_%gVC!jV5(Q(iLRT`c3*h1>O8Ofa59nhn)b+k87o3H zP%}9EPp0V$umSk}`~UyR@7w=B|1{$NkRkp)M{0&(%yif8jKo4cv=-zws@wk9j}j?eaY88FK%rd#CG;`XAKaUiZnm zLylql2kesVPi;fYBg`$OMp659daQvM9~X}`!1o(Wns6K!R`X#TP_AZPvhJ~llEE(y zHLPWP9F91Cbj9eUY;a(sXcG)J1RSBK$Hv33VvTIXc;EEDg))qV$8~UG`rtynPQvYQ zI^z$Ar%oEg9yuB*H)tiaVSlZ6h~c0XffMsd-CkMvMtCVn>56CKBqY*?}(hIfuXL=D|4QCf>h2`;sI z<1Z$2y4JB|5L?!@NDG3Zh{g1k`Q}Nd3}Q;uIqhn+6+a%rzCb-Y2tiqwq%w=VRSkvn zM(@P#&otdvi8*%HC~S7~-wpi9X_B2w83)!OC`hVMFo`Z?Te1LL{$@>rs$}1sGWM@c zPz%E(d3m-QC8*mcQpUb@2x^ihUL%5vZf8@*-gO8nl2&wz@MJ}*5|Os7Lr{`R=JZdJ zpeEQirwzV9u-Ma7v{KlG>({D;vSM$#6WuE@eHD!|vtpd#H$QCH?1qC1Ia^r80N+GAtRJn=P4PTK~GOl8+7t`=g3$oen2}3 zClH!HnM6<)5?UdiOP-xc_`{-Z)EP2msM46s6dLSe^`N3^;SHJp4|b19@Ul|uui9ZS zdp@3;Ow7V?ww)giOr8f{Lt_C6R)1k($!Ihr%E}E@WCPzhkSfd%H)LGb*^Lk?Vo&Et z>PnH&j8lPTB^Wg)7|sYzlAc%_~6t(|^7oHg8r zK)I_l_A+Y^BPp1qM&RdXBO|HKF)4gBswrIoT^Jh&b3Hx7BSSIx%TxG^XOpNvv~YA7 z;dWjZQdz&QUvKO+LX;bFwzr~dLgN;R4W6b)FnB0@Zckv?G29;&!@UZ8PQmtUnAo1- z2`r_+h#6M}TpjD;VIddhM>=EtBKpeC!FHa+SqUgg;gqi*eXz032vRPIbD-s13gi=1 zWEgX8L}oCM(*#8%_Hjdm$G$`gk(*}tx%mt+<>J$G@!7d{J`+#Q1aj#BxzaBH=7XwV zS5xB#vEYtEHUEQudaC8wb6sLvkMz!>r1lZ4*kPvpO>(fK(V ziv(tQhmb`p)B*hZzy?{A6e1cS_R5qQ#-_*$2AGJ@V}9sl>S=Nt^y{+g zszFTSufqGpik3ia{UTCW7WCD|iG^C}Lj04e1JuG77^hAQGM6c1g;ZzLOS-h>s?l0( zGZkM6K4dgTbf%i5DjTV>0-d$njI_mQG7eD|I+r(^(nuEu;~TMLT|UYs?KYjtWzsVU z$itsalYYJ6cO#FBm_rL`zb@9K4B`Y}t>L#KDc5W?EZaAe^Zzy6_sAsv*~J(D-}AoA zyVsLJ#5vJ*y8hm}kJbLVHe567{G7ARamxN-d#&vV^QO|g|M&mP*tDyGX$>7PTsI(~ zlM837SSg|f6g{lqTzde6fy5GAXZ5GD@O45)0NHYj_wx8VWl`Y}j;eZKxbm!1Q>z)v ztH#mm8W=UuX2d%SOf55UkB))06UprP_+@@HIg^}Br;xXo2Px z$QJSnEz4~AJ!K#yhjm>SBdc56E5?x>4U8DrXN=!eC`y@O`p_uNXK0WgO(0TWIv;_q z4C4tEBAp6k%kv49WmNt6!HO4Z#!H5OK>O0fcq=J+9F>(;!s=qWHtYE6#7{lqHTqRhtr ziLzIoHEj?hJ$-!Fc=VWQe^1I=_>3O)Nxsp&gN*NF3TuX zkyEIgYDf|=30ERe*@%aTsd88U7^V~$yY~)sjv}O_cA^(!_Q5t|xCt#JCK|4*g?fNb zA2RN>v3r#wt=h?y z(<3#qLH`1U3TZWtZZg}J!>B8i$*grvG_~NGZ7YhnjNM&pwXH(sRDPV5B#6uR4?YMt zN4O$nTNN(}a17thBQ|Xn#}0*}95T8ZQ9Jld9CuWfL{}ABJ8PEY6h5Y7$2VFMlLRVc zgZ8wALimmGhDIi^tqp4+orO{qBF-Kg7@?Z-G-l!HktlIC(K`|lbOe|kSfw3Rmwq9` zLI_)IHT<3;>lVs&(1>#R$|-(2dF6st`^Tfgl(kh@^@5g@NKL`swp2Y&#CJ%c6pfdC z4XB(}qmHep8~AKmwQe?2HOQ|}H3iZKi~x&aqwF_sN9<{{52)}*)ZI_pMt3renH`3- zlWENFX*|gav~}7^nF^hvIR%nsIfW{+s=Uk755c*t8qtKk87~nP+HsLx(&`jApj}N= zaN7LDn{j!DEeTHp)5fyKmOM=xYbt#+KCL!3QeOIaCX-%V7Ev=BLdPLX=a66_nbUF^ za!*D_WgW>Lm$B2G@5G8_quq%jV4$#iCzj`;oQJY$MWF$g)xb*H`A#gKli7*osfMtS ztSe$`p|&+cUKdjDKI70i)`Ct5w73?-dMOB=9m8PhL**r{QQa)e}1g72E}FrW`vF z=v{}U%I>5r&(u_-(OQE+^qy8DStL3Vi}sR8(zlE$$FO!dLaHf;rI5MwOrrALI&>?l zC!ax}taXs@B~+Q?vRg_#1efAi$<-QmIyt5Vk>c{4+E{Fx5{HqPq57OV9V=hzqO1sE zopnCV#xZ+#?s*z(+hQYATm5KElvN_sqw(2U%(ja6>lCR{l&BiD8~Y$#$9yB0p&!U*)A2&bZu?v95!*wyG3J}d;PL%S8O~MKHxwKZz;1?&ouZ;6 z;tlK+VUdoi3Le{wYS_+QG8`Znh2L)5vYny=LF^QT3!87s>J*EwR%|eqla#~urdux= z2pPH-P5AeXl)Ou?P7^tkum|uFzI+$8oTeh!n=Ypew4JI8NBMHXwP)nX>}r-%R7E>C zXE23AR;=KWEq7Xz5`1%(Tv-=NvT`5waTZdu@0+{24n+yyT%jt9*>04gs@ca=cdjE; zK`yx`U%iNhH)~d-jIHAI9R+&T8>@)@Ah}S&Lar+dSa`E0Jyo$YAG-APf=E{(sLEo= zRe#=7Z8cZ@g;#5m)Kt6c?=L-V9g^4~DSaG;d{-6{W8KH`ambpTdGDnw>yQ*>x!7=U zK*7v+CCTEeHA^YWcIK}xU0$0cPU^_TucP2Tzlvasuht}~h<4XoE-kG?Qb&Y>LgOh) zZ)GWquhu3h+ij0tT3m-DX5&SrBNv+GN|MD_Ym>xAeB#o=Iwax2xOkXCU%f7iuht|f zN9;{qsdX5$u=BF&QG>FRUY%pHP7zxZgMlbkm{N(Evcq17#wJ^_x2K-O?iGIJ_oH~aAH@&% z1;|-2xV07iT4XAjg>d@e-@oAa8mA(3a$ZObf!#h3Mqu(5Cn`OEPQ?zd7Ut>yF_I? zS|MY2>_-2k5HInv9*Jzof#m4epgf2%>Oh5XHLQkUh0>I45SxHiBwIMHpoNpSrHE8- zaFxevkVzO9Y}dkhHC*sw*>G*C4q%^<1Q(gSNc@DBB8w0QRMIRh{2eHKN$gaHhZ#hw8Fq*jLfnhGanjv2}{IsaISD?^zk0e-?k zY%=4&!Z+{)_WzHak|c?&!771K;+wQH8toat{=Y2s@%>UXgyStZb~w=2>DQDi7bz1J zhf9g?XgJ`7-_dX|f-Uilxttw~$x`p?hf6iN*>GMcN}!|{OyV@`5)KBTU2H%Cc^|WK z-p4G~Pm7D#IJZ)^gzeX)%|>mKvf;1zbWApIECg|4WUP&DL_OjBC!Dem3v%=XKhj6M zc`x6y((nz1hvC51ufajI`#fc&@_Y(q1P6{)>>}fYb~Q)s1qrRHC%cZ59VbD6zie{U z;>6IvSZ7}kKQulzJU+%p$45qb2FLi(qvL?FAuuECHfp9RGnKKqC^O{PG1L>h@^2{Y zM9;`A0d3%9?}#9vGH}AEXOxFeT?CpOIvV4}1H2fLPV$&+!hbisMAw{KjSnH*p-RCB z4itqqavYZO(M9->%K5+uWhEcs&4w>Q`OufQ5`@UY7CR2YAYQRb}eBdaP4n9Ox z-^kgASVR@EcGEo)>y^j)LdQ(Gm`AIiz{1DKK|Nj~PU-n@Y+%^0s=LYjzZskP+%vgz zT)*$ti2U~|@9(Vr|H9D%YyV&Dv-bZ}H(g9zUQB1EvNu@!|JMFLj^b1{h{DkVYyaQc z|6g1TmhA>v`~TQ>C{tAA_%A4B)mHcqYm|MYBO`*hi6mU&8w9x<@?|GRNgX`Q{m zvh7W4|KHmGhXugYJPuBljuKn@|HV_)*}?&W|KINaClWIb$JqH~mKf;aR1q~t$>3>= z>Hq(?WcNP9{g!(V_YUs$+*R%=To32)ebe_T-`jjI_5G3Wl5fg)(iioGe0zOf_FL>{ z*}r7p#J+-kK6{nTunG1QdyHkh-}8Rj`=Ixg-X-s}_l!5@z1iFD-R|{xe&_k5=k1;c zJokE@gn}FJX<{u_pjam?Ea|x&)hF}|B-vaopj&k9&&fNRrg+ZgX{mf zzUTU)>l3c`xZdu1!1WT>vs{a=30KsmxDL9ux$O0i)qku0v-KaTe_j36`eXHN^}FhR zQ1{Wg2kVyVl67Nsy>;QbJ+3B2FJ1jWPIz}B~ z#{v5f?O(Nj%>Ebl*Vyl}r|f6!eRjouo!w_=Y`?I5)Aoq%!?p))FSR|}cCq46#CBt* zjcTA$nnh zktFZ7v3B6%xypRhV`ye-Ti>{v@)&CIPMYNm#YDH+`^H6nT$%A0a`C-<(PK!(JMDtU z5Q}$ua*H95wEI|_qi;kM`4Lg$M`ZDQgApRnn_1*JBJm@NG~HkXOOIqclFv070rF@k z#RB&uQaI6Qw3j`L;F&ztY_ySQZLG}+Dhi%~l7wuf$Yi5&xP*@r7$b^)cC*n+p0$`< zfhic}NlLuII7A+sOaW8YpXxRIq%=*et#3pLPw2)$f-%k7ynQ1m3CKhKBM|NgHg-oO z9f|N;2CFjIz@Fp?jQd6u?I{h$fl}V&Rpj>V#(wgsm2!=gBL1)vIwEVgd5wKVvXXXd zqp{aaY=^YKs~l#Yx5a27&+?Kb$-sajoY`vd=5xEV@3_bxm-yo{e_UZ6^cZ^z6*C~7 z=8WA0Z5t8ofW!}|r#NF*>50q_C?`F}b%hkSC?`C|&f;Ap>oIl|@6=qQ(M;0Pit6nW z`Q9#R++%DnV04RPoUyH#J85(BC}(UfJyFmKM;eSR5aFmC zKF%3U#e`9vk4Dmmj7{W8KWnS+i-!3q8d_8)e=7cfp^(hTpEi7*(MV85)>hLO#cSAO zMiF^93h6~LpF?{4Dt%fM-K8pH| zD&fIKgDqvGAtNmuYc#y2XQ*kcK@WHgPeDpi?NYPhHc6o&M@6uO1{>9os}?ziScPTp zkRw`c6s;~QiMJ$;`g(9-KmyzQqJ%{}$EOy>M-Y|3E`qK{(JG@d5h2MJ?-jx=o+)^y z5GmogN{WnpRs7a~qd{sKaA3X~)d(-z=KV%3IJS4f=gixN(~CuWv_ri;V|*Jwl|l5t zOzXl9qlTbCYRub8b2ntPL##o#M&Lq1II;~c{67?N>89Am^W=R?BvNl*6w8< zAk6{n5^k9{HX1ha2KuX(d0>Qm>dYHhgCXRg{+I_ufqDI=s{{oNaz9W=fKE|i8}BS7 z>lXQLiFu{(4)O@8z{j}Ayr%K#B^1&;H0Hh=pGF>CV(rN3xY&*_3V$;G6#S|9(|7Jd|5I|5xiFw_&D%B}~u&n(Yb3g8uNIM{k2FK3& zn-=lTwhMeM5S1(O<1+IacELokFN_@(nOE!c1c&IN1)__f7lW}I(qK}{!@X=7c zL=2D7(4i*S%{h{gXupLKqIaOJ#0a-1OP=bit#)Aqc_1lOZF!w7>OMI`5N=0(EEKc~ z1c0KW{RCLO(!*@?YU zN)v35SSZvsnj%j=ak|(>!6SOt7EN#MbqCn{u=?>3$@kV^QXRL73 zEk4aNTD+02ct(mhvgAEZZU}c!a9bOP2^WN>o$5_VXlfiPdqmsHww;6IQ3$#pShQtP z9ZGa$cyLFIyhI(~CDDwu*_kbAbK?LZMhehHY}Qc^mp#Yw%q&F3SmP}PTHRC=h+1z; zKY4!xYwISpb|?#T?wKS^tj}KNd*Ik%@_cKL=V(5Q_S2%~=_}lf%Qt&^i#MX`=_%fX zJ3QUR8&&W`i#JNx(^b5Qbb30AH~NjAC)sDeZ%g&qfLa1#gwSkI+hJYG?eu!)e z-AG=ZWNmKXRw@CaTZpD0`h_S5qF1B?-Wy7Ji`RRuFOWU1-?Zrn$qPbrw3C4dl!6-B z)KPi{O@XgT+v|xG5)Fv^J>kL)hO8dFcq8c?MyTe1rJeKx>LDJ3RPy25YTHSLkuDC( zuGph1!oDqZ;7a;tBtaUC$uNN6ufyoo90IhHYNi?k8Iayv*8N*Bnl(FgBtjQR2NIFi z0~prYY*E%$*KVpGqFv-jUsw5*v;QaVGl;R`5XC055~6U z&4}8v8>3pAO@-(V=RaoDW>MWMo4gq2(iVf)`jJ*TbUnt}T!%@cKqD}l3+--5lOWB3 zbT7hz&2&ghx|YLKKagxA2R(E&ONw<|S2oeHENK>pNjE~8Kxh*k%bL#$O~r$8tjREG z7KO3v;ciiHq(fKou^-M0^AJ}YyB?-P!hPE?bhTlkWOE*-t;K8?sLNd##gZ<_R)fU( zZiLn$s!G=UjTqLNiUhAoS0c5x(6Q|i)coPRVgM@Yj@CXkG|-{Vle7`E8QQE!Yb&%#wQ(DU$1XA#xFHtD)Vk;qu5AxS$u82$+vy9KP7^l( zWe9sQXm;6df^f?bh@XH`CLWBKsVsMmM|OHJSkB+bdpsBp=Wq1w9t?i-H{t8N81>RN zunP)ocQYBy5$$LnOV7jf8NPBRXD~Ot-IyW!|8KD+8ScBbB=<$`6Wn{b2e}t;Gu$ZG z==+K9E53L8Uhlidch#5iC49$yH~0kK4)&*50r&v>SL~bFSF$f)pTTC?bL?q$r}r`M z*SzoZ{)zY5-g)nN@2%cJZ-@7ww;3w{U-rBQD*&(aywr2I=V_jlC+->Z^m+Ds8a+<; z&)uJQ|E>Ft?t8ETu;{+vzTG|Sj=BZ+ZnxL<7*+tjRIvgu>pJf0!V16vR|8f6eo9vW z-ckR8`qTB1`nI}X*L}Y3eRa>STdX@vR{#RFKd$|9?Z<2XruOxenjTHdBW?zlR`4i`to$qqK*mA10NQ+oS#Sm8{tpY=PM3I1%I&wKxmR5?aNDD766=`J>gaTgXHF(wTI9Z-Hydo{4 zg;b=4Q*c-=s}cC9S2|9Vr`1=cT~=OL8^fp;_5*^fA00>9lX+Q5+C(NX3qR=-`ThM2 zk>NxZ?h9Sm!E7#%h>tM>=V28<>hK%h^!j#ipzX#l#X) z3mv27MNli!suRerBsztJ&w^brim!zcdq?OQLE7VmA}AAt=gD|3F`dp_o|sCUi_g#G zCJNSyMHUoZAWr^ibSA`!f`##MWFr=`k!d!oB!hJ{!ULcoDT;Pvm=>EFM}tqTf^}&^ zLS(HXO^JtJ1`&q%ks#a)qZxJ#l^0uDmR6MD^t~W5iI_txXmD`#h+tg^@Gw}Owz8EA z6Y$?qs&zyhqc-LwBvu5T^u&%>dD_ZWj zjZYB2BooAsO<77}UJvgfauBXF!jjT`3@Kxs1yQLJ^cQh0gld{e7)T&QkUVYrJ}CaNUnrsP1la}QN9b`M@40?l~{@(I@Xciio_MQjl=?6 zY!oG?^AcR5ggZn80D*r6RXx&Ek+`C|1r<#NBFk4dTqqEedqkp9;iyIrM}&^sp-F)I zJX43Dcr}Ne2q18{rj=aUYxa}NuG&LR1`g?;U(-Tv`)c-(OLxsaa)`e@|QyZ=M80-pO+4Ab@!Ucm@QzfrpH7`lsKChzW z2DO5IVrmc9dALiTFR_y-nXRHkP(jo!m02vTEzQS96uDQHgTv8tqobqo%`0fhQ_|_NoG|s!emVM4I{glCSC}o zwW zu8@Y7AhOF8nO7r?3EmHd?ZsTDIN@qiP)$#kgB7lx?R;yb9pfZJ+-yz z<=#3Dc%Yyp^$jPXr}4~E25QR9hQ-u{Nja3buw8yOa``8dd$=xthwdxYF%i80jG0h8 z8jW{>VIp%gwjz8xN7AuSSQ6tS7vk;X zz5RGMje*RVykFC_uNXJ~wCN0ly9&OJ4{f>dlkAa;nfYu!8Dku5Wv?<(;fSGyH?c#!cd%|`L$IlmGg?#E0jO>UQ|mEz^KA*yIsMMc$6 zw^*;DYG`;z^|aE=J~ddseHF2O`_gyYt#7$~OO)px8$j1?mn>adQzbue`@|H}^klAZ zC38#Y%PeGL$=DRNvYeE~{^59ZaD2N0*h$aoj{ZHI*4Cw$C*kC14C{m#>`(V$={Gu9 zxp&*Y+%ET8v}c}**V4a)>FiuuN{BVaSNXeb3xJVZew(Q`lkcnQ=K%UbpR3)^CBWNl zzn@zuzIY{u2FHLdZ!T!;LFZ^*T3F2C92T{H8qWA8xub4gCONG;dTTYbF^eIuXH_>x zLtDPwww~>;vueF)F}jCq$AnVe`Hv&dYF#}8;&KOiE)jg>>Vtc@<6!3~&tG6yuT=qi zw-viwgpJB*#$#l;8eu``x?J3DFH z^RUUzpmyQW`?2$GJ#N`~(*vp$GIsk0Hx(oMTO>1guBrOe$84`|-GUnyaH?OqC0+CI;%ZC)9K}|6<+4>|ykUfmlKchm#>`5HPL7Q6<#3 zxnPArcIm8%fqTYA^!$Pfru%IjFckJjB0-fgjZR*m=~zK(SeRnca&+6=tklM@Da1{& zRuP630O=Z54zjCYha9{RO3G15)P|*?7~MWgE6K5fjHv|nrt(ID8GT@+Fw|PIH^~xM zqLLT4W^b}9)lPF%?~;mKpEM14>n~_Yz*dWja0-eI3yCZlfjSe{M6?PFIc1v)wo@>7iD4$IX?w!N zj*Q7E%fQ?@O z0NomLm+O1)gIxRldZH{BBo8Vu2Qi~a-*95&5)gsq1bX&Vy8hm!hB&KpSN*g3mE}B^ zY2XS6Dk%j>^CeaRKzeHcEx$)^?4%}Y)6bSH?o&9`wV%|R&X#5=iKazS4Pn6M|CBpn@y3u6=M@rkq$n@A?(qiG>EIDs-THu^&S=snnf4ZYUOeCxv*kC zl3DilmOUt$(SjpW{Wzy-+dhacGd4_j$#_b@u??8zV}l7n!g3jqrv%J`+5`I0_Gn23vAB0xw%i9K zhGLo$)ci`faxtd)x4(V1a43zHk)hFeG%0}QqyTdd#!D&|!i8jy{=(xhQA+LlMBJ3H z56h~mTuP+n5!TGf{FO!fSg|~@`Pi_m2P>ddvDLPa3Rr$;i~|yGFk$;mC!?b&VLTb{ z9g3!e(eY7Mg(+b&kyax55dq^zg6(O*tN^Vn5_D)TWtA&gBBDu{zx$GjekDB+7~Z@N z_U)sUKGSeKKAs#y*ObG<0!@*!m>9eygcBpCL_46h15(({6WKya+y_Akl zq}1G$Bfkyx}hAxTqyDTtwp8F}!MKkOftF8R~jwwzd5EX~D| z7gOozh)^bsnX_L=CHjJN{+GB{bH1PVUGN?7e#ZL~-a+^PO!3X0_j(@n9Cm-o{Yv*a zx2xq7EiZ2gyFTlB*d;XoP4nBE7vK@_1Na0y(R6(G$9FI6`qy1=-PPClxyBofN1V?( z3r<(VLk(??w>fS&h8+#uC&6MX|H6`si~0_BIb5z%&VnFIfr8WrNP(gTgPNqSW(%ti zJ$@oHHVtdiZwt&~-vg${m# zUubsT=dbgu6z&m2-4QvYgoD8#d-k!@EfDaKe$q-=EuVjnw7P2e3Y(dCh7Bab& z*_F&?YVSH4L4u9dwF0Z@zJ@EERMWH5C0SL!A__7Rs%SkDa=PHq6bXf>8$?%et#^g) zP)nU5M4h1ymrUt##>v%?IK?=Xbz0)2Qcu+s5lY#RlU2>G3nz)(wQ{#J$jLn-(#WXY zqK4bhuw>|1f{rIry+S&_7K@HTuS13W0=YJ@Pgh|H!>5_K%<^h>WuNony+pZCm$C}F za9Uxlr-|Dm-LWxgq=6ANr6~0FNH5<*!$ohaD>jTz*kc!I%MiP$jqK9w0!xYty~JkXcw;%q`{C7KQTHW{)%8gPz(qu8bJOSk^2Ix84L7PHfP)N_QlpM39B4*aX{Z zfosQ^nvU026G(@}h!P5!O+an%BkM`nKu_QpgcVLK&IXdUt=e2@+gMN7Xd>s2Q5l+O zw%!64kAd>uI>QS>tHDT6fzgJI0_sg$C*5Tz zgK&leW7hh%cSCr$aFE`8q`ds8ewpRyqFo8j5fXgO6nU%tWrG!Fb~SfB%T|}>hU@`i zdiJ7a9V4blbA=t0)E$nloHuTJy|BY(HaTUBsjiZFZ1fN(^^L6j_xb z8?v!2VrTa;a@L_xBo{&ox$dwQ)*?Z+39GUCN}Vp}*}WSRP-TcD*QvHl1omu9K!*Dx zJi=|J} zwl+z4>XDOsB(WP)sv45(SE!1dyB;}Y+zZoer{Jczj&w@pZrhb;sYwpG+*n$*UESz2XaB${)I)Db*1_pu#()3W+HbZZW)EtpB2iWDU02q2r?purM}#J zZa8}j`;D=s1voVdb2+%@-`?HCH*OoiNC2rodB}>0WQr2{ip*& zz@zj3DQ<@Ieb_hYyUY7$-e>rK<$r=d=J`3#Dfb85FK+o_OTzULR}Qy{?`)oE`lF`a z-G96LmAl(^{qC;WU3(fo)%cpm{zjkk)6PrI#)eNe>~p-{@gVnk5G?)6U&KapzkaCP zHOTrwxj_{iuyKZrAT=V^BsmsG>x#k?Laa9h%n9a3y!ZmT^!0qR^Xdi0aBmHUIEbq-1;BF`Nl>M_47b5-atdv*~|h2{$!Zo9-)hJi7Q2sMlfUm%F71@=xLdKMdZA7$ov) zN{FXo!g&4$8mE^vj$cTzOuE7{`_O2yIaGRni8UNOe?5x~mATbh^?UD3esOyCN_O^Y zc5Zs+7TGAQpU_NL-1kJ%)DQuiz9h<7ENeZA*d2ip6!)Sb#dHB)zCrUJEJ@AIbPBKT zov(hCaxXrpT}X@uN>>u$ZY>%J;aVa!*6h5NB&v}$ zs}h+&po$$5mU9ipLs6xZRhhK#AZGo{OnrqcV+TTNFmo2O#GgzDe8Yq1Ne5@DD?&*> zRb-S^Lzpl^a*-YCaMSj$*{O{ag_D&Ou-%1iBG|3cg&w>&q`Ade=)zFoYO`~2j9yDr zzJ@fsEHNuCO<@kl{)Y`z$v(2FYeuo0p|jj-cJ_=CC8;hY$}WqYRAe>3HjDcwvIOG& zH2&A@>`l_Ph*uFmBdjEQklJruDftkHHkYf z5j|@#HP+wkoIeMWdk0e1zGaYP4UN5uhTN_iTy@d8EOevkL{!>Z7o+8YZ+LWwSqbb$ z%?L}mOIdfZYr~bTY8qXsJ zA$pyzYG(@-6HdLanw=L1h_>Yv58B3241-aF%}tTR$P~R31A)G~A3B1W)7ro`;;`qz zYHb?INJr0unRB00Z;CTPhU*D}g%8WDWVRL=E1n-oF7|DxZLIP#l53kLIEb;;Gw?p= zLop(~R=0`>(SSwFG`6w+zjfl5daHY&rI*0oDdXO&Xl_%tX-ti7s1w{xD6k`$_O@;5 zMBn}3#yt?BbxI`kd>h))19zX@L=Y8;;KQaNj!hTO(@=Qf#zO()9eXjkXtoQ{H2M4t zg-d5P>{1GBF<6yM^E}D1_3rxU1FVv^jjna#kzo;wm{vf+oSbFRvi@!|IpbNjsov+j zdOzc3UkEH?Lk`O!#LAj;i^|yS z43Yh|?KgU{h=m!a4P0-+qztboSu9>Z;6NC~{)o{Mk6m| zYdU6vQLw9rGfX%P^OaJ=?5mL7_W^a|x5<_VKeuYbuM)xAR~6{icTl%6H}0`RWHyGG z8?FK7J@z&a?%4d#!}ham+*~#`+%H2^-(l;P6^AmIVkOv45fF&eHCFw}Toz}uLdE9P z$@8z^yw0aM&)<8ly8pzjw7j|b&zoQ0^yf`4^j_lMvip6z?{hxY@IBwZ;5a|bao*o( z_+C@%M4LlDlUtgD%?XDQb^w{Sp2}u!P3PvOL$lDYsANmeq+-czNx(trsCxyh5tE?B&eb!s;|mnap32KQ)^qB<28}h%*;Yr zv!%zMEIyu&B7~wsak;q@Ff|}-8%Ledv9vHcF){*=prJ%Gc~KaSUlgJf>9L_vP)NXo z4V{d+3JBLTE9jpqr*H=8EWL+sagv{1$t{x?=!w!pvSxp1XneH%WhI4)(uG88QnHF$mTdG`aFb?;|Q*VC@8WtQ;Bt*tb5`B_si9PYDd<<;!XRcjlv zAyZRz$#{PpKAbVQOxkB2Jl!eu(YHx9yoN@H(nHaak&DF-<9(>;hMl^o1!l^^R8+1I z8;e%PnYGm`(`2JBw(#bRS2I_$)0bCZF58eW8#KnyF(<{XvFOx2w~sZoj&(S6sXWVK z+Rwq9KV4XxG1dW^#Kgdxrq|+QB&CH5W>)U@(Wce`$aB0} zo?`P(iy<*Irznb5mhKH?seG@t9L1*~!`-)AupS1g)a(q+pUvOYguW67GH358}PWp0rD7E;*H80>;!r8gkLC=w({)ss7#%u45y!l-rRB7irpmI=dTyOV zc0AOXIL5}w45BN~&Xr3%bXe?f^(Cp&^H!&veYbb-c)Ph+r@KA}+s)g%Hr~>iGi%N5 z=9PMK)o9()X0>AYkp5~sPTS4fjT_6h;dZm`7Tsi4>+KC>vpmiwY-dyJ5Vq+Z_UUNa z9A2cakQkiPF3U5eK2vvlUhAzIvREF246U~tj2<;mAp`Apr?CaeVY4wk6A8}D;I?EM zd&FxcN!p7oXAIivb4+~_HN-LWZbmD|{ps6*}<|<~=5g zxo)yQpfSs;IE~XoHlxm7E(>0KTBtAkmg!k)VOf-mVi@&Sy^CKHSvW(cySm zQ+f6+zGtn+!$rxgbFPKFZ8x)(V@r7pXr6ZJ?_$f(n%hOQHs#68j zO54!#BwBeC%Hsy#e%?`Sry)xdTQ-f)fn=SO|L^ne$2sKwqYQxG_5H2yuY6zgecAVU zWCQr5?<2ks`rhYzx9^?4-|+pa?-vmz@H*d5`(EYyG2b)3r+l}3tG>MNQD4^gV&6mX zA2{zD@eTNTeP?`OpW^HGb^7k{wfXM$9rW$-xqZ8R4)4Ev|Hb={-tT$8<^5~#*S%ly z{;Bs*y#LkvQSXPm@Av+$_gz3B{59_{dEe}Pz4xaPiSRP-OT4SzCGQpQjQ3&hq<7qV z&fDjGp*QT6y{EnRdIj&@-UD8*x7pjk|2zNB{D0^Fj{h6}>-?Ab&+(t)KgNHEe;@xl z{5$x!^S{LZJpWq$HT*01+x%1fO@4)6;4kwp<}dMSev}{Nd-(_WATRRw@%Qk@_`CRh zyocY-bDn?m{J`^l_gCFtfNkWH?hm_v-~D^;ce#JV`sSrgMw)-H$HMFV7Yl^@s00 z#GTZmyxtUR-rwBpYUUlB%jJS^2oJvn5snKWIW2YICiLpqmBj+ZTz61FSx}jRyFx$; zP6;sz!AhDw4b?Khmmls9Yb)8}LzrfTG~n{C3jMi-#X3j6&5{K zUA?fSdd?Sjv~nFoJYTDB<*E&rg(rs$(qYjdLy$VEIfSenuHVzj`4?+&s0@#l#I-@p zEC##-eqkYaCZE%SqKS)9gg$i$YDip!rmOh|EqvVz?X8?NVdaZvqgIPes}mu^-2{Po z(-(p%wL7%dFU)38r(8{^6FgVVO|x)oeB?wccYL4*x3c$wd1+nKC8C{_EbN?l0lIsr2D{j_EKf{RY~(c4otBj4 zD%IiJa=_mJZ7`#gCEYZj*SI50-KZ(G)Y_3&uI*NhPFMRPRtK9R>`B;s?rS zLBNW!lwB<{M1Mroy4e*C#wCVU{UI2CHG7qM24GkMaIKkMUO}kV>}vP&mF2U`xw$hc zs_wdecPrPGuTl3zN`s4!DD_4~#jlCI8CGQgcL>WE^#xi&DBeH6vgOJfljgbc0phE- z9l}7&@{8rMfoMaTHB-@qKM_9fNA$h8*1M3uDGZFK%rLXmSWLK)TZO}I3HlUzU(GHp zWvq+Wntui_nw2kBJOF@R|6=%>-w0W&>i)7Z&l=s|)u?lKp<6u7^Qo%li4jIZLk^B3GO3o-QLBfbqgb16#qX(8M&MluXm6XBsoDTD6wZ*X6EB&z-J0 zCCj!cdG#Q8&er59iN!U*69ajDwE5FUkZ?W=kS^3{s z`OV!8CL!YEB~04pJwH!Soue+{I9J1Br89`%8#@1 zW32orD?h@@53}-FR{kL?Kg7xpvhojD`TMN=04v|m%6GByovi$AR=$IkzsAa6W#zB1 z@|RipHdg);D}Rxdzrf13vhua8{5e+sEGvJ8l|RkO*Rb-{to%tD za#p^Kl`m!Ge`n=0tXyZM&dQfGIyuMFR6Iq+lTVt|T%D*C86Lq!)A?Npqg z0*U-Mhd@Oe700MJO2rW>4pVVA6?akLqryuCPlc0;1|0wC-){V@;^#5^T*J>3_<0;Z z3V!_fIfb8l8(-)ACg!76$8T|8;qK@4!$n+hJ{r&lRrt&4k;xze zF^7P=k^}_J*tif)j0?%6AQ9Swfv0iy>jxhD#=tucXx3$Q4v{jjMHjc5wU32X-b-`( z`6GKd&EFL*OP5IvMO8J@cg~-d`~z_Vfu#tfo65mgp5T8fo~cBgk>n!oNAT=7zVp&! z?>YZnb#pl_2ou*CZm+*!vI|u3tNhzQ@lfFS8j>P<-`#sT$=`mqT&IcAiE*G%Xw^u5 zB`gff6Y0s6V8B(#>ZG9JCpDIq6~A)!>+d;F)u{)*WC6vpRHh0p!@??H`P~G}kl^=% z=GXTfqK?ykhBo#r*@M|}6s=yzL0~$3_WEL@)Nj%kk{4L0Is;=dst}eoZwI>>!RRjr zOZ1nEckSgAt#hcX+iW5>9tpwiI_-}KfDBJCyfxuwY>%blqp7i^5FL$s~J8_s7b0 zYfTMla5U-di)}m@>_6d}75@U;ZH7XQc+AYr`d20Yd{$v59lh-Z zY;K8{C1!IA0{7KAm`+7-ESbQutngN>k5~Y%&i?E2+rHFt=34i51*QIC@~8Rb;6ASN zY?tn6YCdZAVgsL0)PTf}zEZ?IG@+CH2{SE6Y(&%;%HNnqD2O=biy}7BMI^&uO~YOY zNQPC0y|eFq1O0nrYz@Y=%yQPh-8xwXMx7owu$SwQPA19&bC@CZ018N<3Z*M?fDGFO z01MOMi4e37%Y^_)h@mv#xrTd%{*h=3XkEd{epoR^yXr`1PUn^mUlaBY;b8qmRt)+M z@zYGK0Nl(pMUCj^_oLG!q^Fz9EkfX*0I9%pWk^x8L0AOAD8eSM2vOj_B35bu$b^~} z3ybG~OqYlw$%a(Km?WbdZSc2jb2Xc!vreTgGpc=BvPz658QN!bv!-&ToJOl5W`tF0 z!)_LXVWzK_bu&7rJ^L;M?&AMAl+~ySPHCqB4uwdPs-9+6=Tx2Qm~CjUi(ODXwioSl zw(H5Ns)T^ENA0s=RW1-(}c)K zqk+~g4v##4)^oF3_in3Nrp%@pUGMX;=CKT)Vo$@Gy`>3F9X2L3;E|UbNZQ7HW>#eH zwklGb#F&t1p9b$kB?8kW7?gC`&13heoaDk_IK*2*w z>$1qpl*V#m)alcdXXE%L^4X^Y(vp?pl6x=L#q($>whwgh`a!EP9H?~<`p0JypZkaZ z)aJ4>%B-y9Z&cXW3IVJcihLP%^y|6R;OYwfT>+h{-_yd@HmkHmfX_lZiP8S{kKV2Eez{I#wiUtYby?0aejLfX&c7E)2xuZI;G3Yap6R z*sIe#IaC5YH4MeD2;Q}Ip$Npa3N{raim<6B6C}n8BH|!i*u9rK*>3F`4!E%)?-jN= zgz^t4&|7)oBH)si;)!8Q(!C+V*q!wEZ*Le|h+-7qw+q|xwoZ!Q|aDRs@^!Q*xQjq+PvfeE&0t>$McZ)Zf z%`P~dxHc^iAOt23809m#3(GESZy=)Wv_3@P^$x@C%^l`G&-pImCVqtfYh?fXmS@oY z>+TaR|EWcCtu}wT`F7Lf?%&!S+VzoLv0c8#pKWY+e!v-NSaZA=ul=w1PoG@_;$lM5 zA2$$J8(hfKhFu^wfm_`LrU`OBNRaayc7e(k@To0hev#!cuyv;;+GP6SVMLqEz(BH+ zKBfVyfB_`rLCBsOi;fF1ph_a`&xEmOGXc%HH|UX7;G%YQ>WKk1dr~a{J&$f>V0Hq? zoNg6JE?$;Yk-pHg(9ZlxTMErYCo>DvEKy9J-hGb zUavM`NaxE*JyfDl&5gg^bhve8QYc(wMBRglge|m zl~%yUmoftacTp{yNhEnv0RB*f3?+T&LFNX9d!5d$_&BXod(uF(ZgB8gSoOz(5k(wK zq{9ACw-OrfuS!g3`=0XInYGERbuThh7PhX98#iWs*;ODj0>8SNGWXhs>@^?P)!I=g zNdQ}gpK|KqKpBt6(=1hA`R}e0)bU2VDrj&&*N|WKaqaQLxGbbqnkovgd`$a@h1H5# zBTBX=+&1+S@ZuE&PZS^7QU%Ktxu0#gmG^N{XW&+uA?hFPOW;lr2)v0){$$9mSKD{- z_(*6=JgbOuKhq#D`M9Hpq+(Vz)HNK&AuAM#6q8XT6NuGJhU6$4Nuo5u{Z!-Ai#`~R zx*ji!8`1nD3ABQIJ~4P9FgiBgn;rs2K}7<-ibu92vyHN!D04sEaBTtlYU{M4OCdj zX6A&2{0%>@uP^6-znWTOUIl)@M&ZVLu9#<2I0XRVP5~|-g;lzoH{oj_oe_w|oJvZf zkdNc_qBtN7LncYv&AqBIdBw+dVEc5fp*X?7Cj-bf)5+}hm_ptui!gz5GTNOLx0sCQ!rDO<2E3uj`&zipzZbpWm-Sn7bp2B`zg@n!EiHk#Z^bmxoqV2{{k$)+6bn@Avsxg1MQ zVIN~;2dLc4*q+!{$EijA>G?-c_5B@Y)g3q}z*Hthipeoz_YR*+VXq=eQYw0WbX)vv zgr3s5hr#J!7q54j7kh95#KEhWhbP__#r|0I!!y|r@H@Y#DipBq7bv1v=w-+ka<>Ra zTzIhISdNtkkZ(&^D6nj4!1^iVn7dPPIf6$qmZQ@`GBVY=gk%7>!($_T@#GWwJ(tk-hq}rM&iT~&b1(_S$8qB}ZNGl5_@+)9)ZVL9-zeMJ z1S*Id^Er-M=;q-y2Aa@90H;CgbOGBOhP(!fn zGLpdu`lIj?9P~3%SbY0s@q&L?30Qez{|F>dS>1>&_gUSc`>>4)bec05PDCRKAhpVg zMBF&oRG(_BUt1-wZM9ukq~{4*IKMm-K$v!htZ$_(<=n?yH+3%-U?deQrig_MLxUsZ zGN!EFOYx+X6cZxgEhryy_2-)lkh`+5OtfYm%dSW(;-V-8j3LE+)TKLh9vunGS~1Tz zc=ukq5LR(#JLM1g`)kE^OMpG<-KuuJE3H!bHKX4Xv9yDx!+ zoQgaWr`wBK*B0fkH^%kSjzTy~m@QLvYgO}hNtVM$psI>LZ z%~>uk-@Nh|y_^ZI)vEO$Ld`B1R1S5OO**Ne5KvzFXvQXoA)}L3e7HI1LXN1+!Fhu@ zIGEk*L#`+EW@HXRg1%y+ZD^UL4B?YJYXo7?lDH+eK=9kKUZqBhVSA2CP03?X~688tLHGMa# zchqn0JW#!miqy!`032clrcR?YkHM9nZ_Y2IUYT8$=0Rir28zH^@j%V}zDxJ&yD(z} zB!JGEHmfwi5GRp`0aFG1dE%m~G0j>{q}7jXF2yRyUI$ss8X`jeLAHjbKj6|EbzBj2 z1-dNbX(R#bOjsUDBrAEZC(Yn8LJ4)2G~?o9snHRD_RIZg zIs@BpRji&8_UEkP)HbKscgFU&8J7-O#97#{H(;OS@6@MDOEnEJEoh9oxMJ8=jG+fe z*^--{^_k73u`J%k>~3?xv(kbGp7l@Dw1blZtUKJh^xZmcY&1-b=DY+WRHC;Z2K)1D zMHn*Ps4+!aAFCs=xun~bXMM4+NT2()g3i(G(PgIh#IC~Fe;`eLy`=HD0pAwqN`l<4 z>fF;X2M?b#CrKASj$pL`qtaVQ7pEZa_&GHs)k|krTLTsz+%M~nr(iG|?s&S?W5$69 zE6ZD!VLUi>4%>!pieTZvy-jyLxrc-Of!BAL*DqvA(ITU;?I{dr)exty5y$%Is6U3J zO-tDYO3Q{D2ThB@PB+^rC?Rdi_|10c5yl7nganKHU=sIB4?F>5?0&7hIRL=59~n<0 zafZ!M)pB1#WswyNn1!d1<^X$WBik?cRz3ch zG3m|w%^D6RQK9|)ox7i2=YRCaE0GvxCg}mtugz3KI z&}2Nxy+zkDJz(0UAYFbcJ*vWn{ z9>po1QslwEm5%l@lAR?I+?(`~>*!3F{-+HS1D)j(xEd72@exWWZ)8(v_9P4&OI~^F zQ`@QtyNtlr&7_5216E?TKZ^WOmk2NhBZjkgDmFNlP7hx+?qMaeQP5>({|NU+e%EI> zkK-AxamD#(&SAd0@gF>|b^l{SyW^RbUv_=eb+G9@@wnqQhp>mHR{BjNEQej6af~*# z-haR2c2^nN!NmGNG}LT%25?|kS;SZM$LDGQK2$%oBd8s8TtG6+WoHrlXoGAsg3so* z0-jJ3WjpW=WgrO_9*FqNErU*U>PK#O1CvCl0+Gs;GRGNsN5J~PrB61fg(Iv!RFt1N zUQ}x(ykhx@Dk>I04?8VVzNxa#^?Ppnn_3mc@l3wl+V~b>GJ}h|S!u@dz>vN1EZ@|A z)o3}(W28(g6F}V@xZQzn1#t|lPRqm7s4cY!8wBO&0MxT(^p@jdT-Yhs^Z}j zolo4p2U7{eI?7;fEG$d?b1n<#7giBiK8(=Z$;moJC3GPaKS1kt#+C}0h6AnRjWqiz|P)|air5|}dP zR}hfUcOWC72sz_-Q2G|Fnj##$|Lste)-T@HEGu$$DSKnu=v!v5D6hjCeOIvuMZ{6=$_iagDbGI^$ly;Z1;W??slvd(&Y6}xv>EV(}YO~mKl`x%|6i#%mpQFBh zXS>OuXBo#rC$F!tMJ25eQV!c#Y12l?;G#%g7v+U7>|f4qXlcxzqr2HNT)9 ze0rl!U5`qw_Q$i-nN8ZXn9<&)v#IqwWYp@&$OaMQo0G*%WF}$&yqYxV%dXAjtE8}j zY{f@UbiQ=-_DO~)`ikN5R@lmwJ!$>mVo?k$x}Ej=9ZjmV(%0RnTAZbT9$XJpIk978 zZro384*Wd>3~cN&>z>9a(-}Os%JNkJhc5!PJ%=r7`M_5HJXLV^(%A52i zyo25tZ~!A-)f@2M=j{Lz;9>6zynDSKZxgTp{|Enn@&CkspZ_+{0RMvj3jcZj)BMNz z_w(=Of1Cex{%!o5`PcD3#lMn&DgQKoi!bnt{5+rGAL1wYF@A`T@n`rDFY#S`2j9jY z;`dX867TT*-<}^LOTxE3f9?62=S!Z?0_pFgo)3E7>-jIh`TI4`FM8gDoQbdY{J7^C z&y$`T!1%lB$$F+e7lH7X@C-CuTp z&iyI($J`%szt8D&>XYv&Dt>4_RWWQNhMg&V(tc{|l86bi1JY5EE60sb!VE<_h`44PkcHln(#W@9M zBH6URGPe!_D!QH(qVbsjJWOQ(1OkW{IjE+9@ZwZW$ zoU@R>lR0O_$T_PR=RGns3deAPMS8=4o+v^zY#Tv>+_w}dLQx`1bH;eLfF_P2K^%iy zJ3k+VN5x~^Ma_)BNi(xjaA5ed( z7`(InnrFQ$atJ>5ygo5itn^T+(tYU|lILAYq~(!xGCG<9)^xmgD4If`?kJH-ObL^T zw6b~2Tg>n^oL4Pu0+!zmZy`xiRU}ZXuafMD*q2*bDMB2LuVnqbIV=HlLT_$4zX0tR zF3Bw4<*D9tX`q`zdSr1aKgljWLZ(;i*%QC^JCl3=SiMPq?!r)nkGX_Cqd=pPR@itv?aEivSn( z^TlfB%hgP1mm-rAZqf|{@1^)CjMb5DX%dm$)FWWvg=g&M6}0q;Dlm^kiAkXRs|n?S zggg?`SJv+l>6NjIQ+?-6C0xP@bwyy8q9r*Orx1@G?5JpBh)!O;d-S>BU0f8Pq^{za zC|5t)n~97VDqQyhw&a^_IY6hTMeK7uyz;Mai-jv zpDeZVR?u=bZ(@73>>xSyn{Ot6b1-sFGn;RKnh!M@UqeJ0ChREehq=o*BVbC4A>L;# zw<;tNs^nkI`|1DY%;s7@r5@tC(25yzI+sfItAM_#54E#-B`y89#`V4p^=Ctbo%#xy zym4zyy)@Cm6fEY+ZCO7d zV-Z-xA}~~31P+!|WdYME2d7nF_%IMp;N%FsYXxNE?E_q*8D+681y)T27UmnE`qWjO zsWO5aiY;UomxVC26-GS7v+pi+3Pa1wLi&24I5*g@xdH9tCwL+#cMQJ-JNM{!Nq2GW zG+fH?jYVj}^yU~ro9m(0XfYvzs%ofOVU$m-?-LJkokP5yjThU`S86{CI1iA701?Dc z&Nc=A13;jGkOl}q{QiAbZW{uSpzjz`A4AbQ%7|D&@Bjq};&-6%wgQC*z(nW@fQcvp zn1~X9i6{Y>h!TK_C;^y=a%0duz$xOHYUsRIvCje5h;O$9qsLI)!tD8Nz~}+Ov52Jy z9LN9j0QKTjX@;r?@Jf!rD>(wMP3a1WS$(EIC53Tn zLKAm*0t%y>ll)4d_`e=xxLpLINX7ukST2$_Sl)H4i92*091R(8zQd5FDNog zA`bUTF1s*SDF4=R5X|mJ_4>0bD@adT{GTubv#wDALXat(K7jE!=HR(_|4@7Z!R#x5 z8J`vU*AVS7wDhO}gCeA0Db6kyoQL=0X+JeptO(kZU&0#;Nc>W81@@pF&x4~8T-3Xg zo0|tB3gYEgnK2gnO!l7^n63S^07Ieoe(n&)B$Q{^c6 z-H*6>Hbqc-m_4I-hvj<+QR8Po+!%K!Iv`vZ*Xl^HE@M;`6*YPgD|g-PZsOY7z;>vZ z1FKN_L?U&TuZjwmUs;bZOvHsFt|o4;f=7p9L*;BNvxXhIaHzS7JJ^YD2^EIm zV-WbCU7UeOOQE9){5A0J2WAbKvHfMyN>Thvv=XK_!1^58lvdk zE2~1b^yjYQyYL9wi8Dc+GYjS4fQ7TNTEN794D%*ti5m0fk-MEmRV0f!(+Z_uj)=(E zu^;mb)d1;lvcjpgrGAbh$O$|Qa|9$d*p^mM$u7_HrvGnIoKTm-d->q z12O^GPoQq(o`>0P(fkClkCsoFHyzn=gp`sb6ut2i5ZV4WWb}zf2Kb;yIhpd`UZ(i4!sGZfK%HbX*(yeL0ClPlbDnOm(pdvgW zr1NVug2^%m2XIUBaRLK1zJx5QtT(NzOqLE?i)zSD)kCrbyF+MCVDfLjMU&G~(n#?x z1;SI)S_dRa##7{<59|{BNguO-3P_^t|1o{{y23k0m3D^Jpki((1cb56v(*= z&M+@prOWk7ZU(RuqFDmE?4OZ`SGQnHKUEI}U{pewj+W9j`) zN@`?I_pS$+AX*KzgnJ|m(y$iVQO~Y`?15%B!{>oS;g%=YyO}`yYX}tVLC&TyLO_Dr z4m4v&ID$L~fUw~jfq%${rt#NpnqZHlbpx+5Bn5XMfHmC*siTwvwKBH{1PRJdt)GS* zx-;t9?Rgje)MeEUkW*&@4s+0hl{hY9xXK_rK8~LUAKz1QB6Nn@KAzc*|gkgBZ zpSq0H^%akFmR_Z2*+qeH70<%q9=Thbg{RiLHWDDz9Skb~$0$*uK&t0X0F4B=Z({*~ z8jB3S&yxT_=iJlloh1Rv3wfytFzst9(quhP0)(87(@(FT+E@Y^DJP`J^CUso$^F|? z>nBSR)aZs9dI2)mD9@7w5ohSB^?NrKKm_a<;546?aiEIS3pz>y)aV6jw;GWFa`rq4 zAh9;MrzAm*Hc+~Q3eq_|UlPdF2JLkPPksKLKD&zh!0$`GvJr-2P|QR`a-BhYQAOgb$7* zBV3B@=2dM;Kf2yV((9>e>7_`Irs7N_ha)@L@Kk^x)51!pSaWH|$zxDZDKZLZbB0vo z#lR=TCJN~Qjz+4wq~E)Kj0u>nDImJIibRRXC$^Iv84hiPf((!ob2}ypYlA`;fCoE- zS-8poSWhVVFTt|~VO(MmOEQ5IK$&>&v~h@2m798c{V3}Q*ce#OaI7Ohm5PEBKsXfM z$p&SF?YkM^@^l$-85)&DCN4IR9guErjq)Izh8Gk9$lyZ5+)=V~r-iwdwMAU;Q*Imp zV-62Kc3QY>c%+=hMlyvhd>|cFj;2j4Z`$jnl+vf3HmcYQ- zouq=(9V@`D?9__69RXqeWUXv$MK77Y>~(-27LY5xD$g78wVBdxlzy!i@ypkM9o^bd z7NJm)q^&B^8?v@-g;@oe<>ft;=&j7>>h?0dA$8kUxLSxlmIqFEXQ0gS7S-RD&TX?) zZ%Epb9m7%Sa&c>eZ4T;;f#I%au+EvuL%~tD*1Z^ASdqd*3Epe@&h?H;o$ww&( zteHibW|G-*7Pd_izHvWka;-=zWC{7U%nl4?m8PX~daPT4GP6xmzOA3mA(ipbF64VN zpm`nqwTjekCe(e!$NwATZ!`70^`vzVT|sIGBz$W!GrsXZTFSQvBR#YBn9S`4p?3it=qO?;NK;2Lf)Cd2V$Y{kE;N!(rWLYl-K$ zmuw5mlDqvyAM*n>AqUI;q2SiiYK1l7XONuS?{^>2r9qd+_;hy4qL{! ziqvyYZHu9elJ0mItdQ=r%jl@!#s5#wJ=gpY61^Yx50^xEyqqo$KIpZQ&ux}PZd)2D zbJII6q5Th8+doq)4O}sykv(DDS{#+GmXK`IkDQ5OP?c9Vm(4ybEblH}sASeuWEQMu2J3`e*e)|0IlX-f+mOT7 zHbAx_LKrjiHKa0&g|(&m*oY$c%5rcxdTw-dG`V`}p7BU(f$IzrxS*DPZLt_52eM@xA~gykGac0myeto>9*kPq$~E`|sU<2~4}+ zb-xuDb}R0O+~?d8cbEH!dpFqK`FBSHcQkNE19voVM+0{>a7P1oG;l`)&#MLwuOqJC z)!^qFxE~^3yckk#M9*^nynh`L{SL=bzQK7tDG5ngNGja-J?n_ncQo*PLj(7B-1pq; z2)%dFpZM#$-t{*6Yd`%pK9=Ub<6TFTz4<|`m;2^{bp+SD8rtZIft74_NkC*f_qPYv z5jpQ_I8Gm5%q}eCZ?Mn5wQn8a@~(!%^m!7_MfC8u53D0NzDT3w_{;sb{p*N=XYw}m zO-RydVFG!k-@kVq;qMN|A;{Zz9v_~Uxo17=fP-*3_VRd8SqIEU@hOe_vpwsG4|h2Z^NyoQh?bOs z)1GyNg&R+tlesT@))Dhv{8Qq-!mo4mC-eoXBKM_k{b^#8;u~B?lcVU`%{npxcO%+#C`s# z{unV+c*m}zFE&9F{jdwJLuQcv+2lbouQ9D`=t$;_B5Prh` zyndq@pX(F6dr#@h#*^r$Ni-DNNySePPXzGh(+9r#$+i`DdTP{Soi{4?s42tikay=SAO3fs20xNcQ)- zf5R=dyu0OIL;!f3f2ZO1nq3W3O&50m=5B6Ry7BGaKk)A3{vW(Jvy~~^`5%rs=HoF8 z&k0w_`&|)4sYla#M7diH0&{&QKEScWV4s5fS0PO<`3AUjY86lwfJ7kpaLT00VAj_d zWJwW2oB*bg{qQ7w?oo$NFt3hw*ItWPhanrh%w_U>g`?Y!{7=TmMlMbV!xR0}eZg*J zFgPu1m~=*{H8LEakx zk{$#!$Aj?p(4Ku~>|`X;sPnqvDz91VAw~p|!V7vNAcJeN7T$^fZX$X?fm=yD9`Azi z6VYTEz9>Zun9~GZ9$1^o0ipul70ZCZ0cOw1uoQYkzvAMev6ySG#fz4yRgLthO1Bh* zSC5#Y7!1l<2guVlf#70N7m5XFoL2}Z=wPCT37kS`7!Zt!qVm$j*BiNXUg#IKSI4X{9R6skkwFt_(* z(_hGVn({H63`Y*=f^I}oJYB!aph`j+WeGh;?nan| za22@Za32eIOA(}I7<}wxSeE%OVj%xI)8nkot;rk!kWxb)U`^VAl)cfkrlv-KQ4jfy zv1!DnNbr!(tq|G}5Lu8Tm4WvFqen>1UdgYnUcH6RK1ux?2@+2A#X2!i#YX*uJ1DZwT9!-Q}HhlMY(!7dDVp459nW;T<`%vrr`F)kZmx%~+(esPt)FS>fCUS+U6}$*Q>^ z>(#olNC#ds1V1TplAI1zh1NgcgXJV{j+k&_YC3t1nnE31gbq&uX9 z!;$~*V_zbQQ}^pRChJ;_{wnq;a9xdr!@-^S3zA;PjR^|bwF zg@s$fBA}IoU`X7p@6sP+N}&Bf$?KdZZ7iZ9#d!s#u(%_KS&LrEah}R8P*Uu<<`$KK_qKy7=mC;SJiGxz@hDAV^Wp+8scn}^7>?0bwZpM))hzW zQ4toX1cSlQdvsF|Z_GLv z#*Fyf$H0Wbg_}ABGjGttgIXjMeC{KVLZJt6ru$9smh@UXEljb9ONd0I@Q%)F)AFcX zxe&-ur)G+EpgDm};oqy)vtj*bDbX?A{-Lv((V#KG`%7}K<0 zkVCZFqTjcEd=pON@NXR7Fxi)mZ?bWG3pL))WpL&CfRXH2q4GXZNdi`*!{8t`{`E(fQZTY{R!3RvbTY zq`6O*@BiJi?zsD~yQSq{TE5xxr!60D`Q4VcwY;|F$6KCgx!Q86CDEd_9BXmA{)g+Y zU7vNm-t`LCP1lTT%%!@H!@ltE&41VY`R0!`zqk1}o8Q#@lg+yC8}LMUkMAwO9bEU# z`_A|dc>mG+RqrRf?`Qmw=}(`X}u}g6l>C&PBtBEYTB(ghT!56 zYUi7n6E69aG?_L6=8zNv#4M#TN@d~hn=gh`m`}ipaQ53Qz+f=7UC$4bM$u_5xR(nY zK5h|Xtb7_OX3!cVyw$~A`SY`%IRBlO9(zwRxjAo(|3_7_o-)Be`ByV*S40ZIpz(x8 zjxdr%ye0%O`PGObF_*@~8en8I1F-}~c`_so%CbBhRYHBHgy%Px(C&m|qrRqs`~FTF z_fXg$!KRIIk4|2oW0e9sB3xq9a`eH*Q$Kw6)em=W!7-+St(EL!3b9peY*o#V=yOWv zHrki&8-}@13S!$SM=`ArBbq%LJ^PnNql|64%2wJ>$c86Sxm&uJHx6R242_pOfn*81 zQSFQ533T=|Z~IcqndM&|-7a%Gab1-d`zI^eBZL*cR;YW4y(BXB<6*Fe;njMsthq(6 z8Tx_U`K8J1i^;L$tH%G(C6}SbSmVRWKBv+Y6=tUxk)vB_6x_} zgC=Vm*)DS%?I!}Y8w?7oXfR3aMPD864Ufh~V`ty{2Kx8L;q5YK4OSw4s~a_M^;^5D z5{dZ3k`iGxAC4#DND7HyHMEgBEGMI}^n}?{&uy2tjr0?Ku&$mOu(zY6n4^8gsUgXx zh9GrTZ8#c~`p$kF9TmNG|8}|4)KH@SZipCU>}ik=M3uO5A)J&3 zsq(NCM0?MEiNeA#b#I%yjk^AVF4SIWvv|$oh9HELpm5k{<0LIGm+Nc^#<`G6k1C`6 zVsNCt4(}SC_%%J9i(~>eh5?|qyjcx0U1CLAz9XUby90twRKifk$e*q7~0gBQioy* zKl-UCAK9KbIiS=dF)aQIlt={Z)B}jOl*1uS_nd+Yc>Bq-CAV9>36luMsi=k^#W1Rr zyUG0Xhc93$!{-`uFk;Ze;vKv9= z1iVw3voWYb+VQ^r(X-o?cD1lb`9wu!_WH{FJj+O|AMRkPOjM{+Ma;D^6Q(Ow*?S%u z!w~3;4W}o^M$*xNxG*-69-l}Hv590dKAIL%gA*tdW1}zJu81smW|21(dhDLPT>J1z zOG7)y(eJ_vQ{*@x{INPA#JG&M1w}3{Ba1NFJf1w~4~|UrCq+@4B8W9>Brqt!aVb z^2Vgel7bnlO>TB2UlA(2-5k(o|A6)b$KKPkT|q69ps@#z@8$ebd%P^_L}iv_iYl`Y z9Lp0MrEWg^#mnqpWOETMJO3IXXcm5Tb2#fA0tS`jA1K$vMQaXlu8KS1>QJg}WmP`= zU1Pf*-n=&UY1AqOG~U;SLzMrxMb7h8X^4x5sM1lc{J@5Jk$hF5=3LV!n%Z~Y-t`USrF@a&{f@)jWkZEqEq6W6Y)X$; zUNg!)Fs%b$N|AtSw3FMrXkSXgR(NQ9id`=S{WAbl!rY6zHZ}9!;0FD)pyKuxY1naB zD-?3`iy5@?!fFP85)>Xo&yckS{m6O?6Z(8jp>f9yAS6ZH3HC=4egWk>=Hd-AUD9rP z!z@g(3uZ}Hck6BIE+*ep4f${luKmC5y$N(%*?A`TNN@v0h*GJ_C8}K3lT@-qlOXWk z!&-K^jE##3kPrYaQY|z{f+R{LK>?shs$8zVv^z;p_M|(DbF%c7?(|IOjML*YbB>+F zDLd&t)90jTvUcJV$4O5oeWrV|PI7WQ(=+q^_q~UAAMhZNta94vk?fMVT-^KL{lEYJ z{ofDc7c`{Pcx)hFU?$jff|~*c#)BsjQR`(NI2zz0&O3?-H--izK=3qlg8Aj(jAj@U z;dwViM1|@xL^KBxZm;K#`niO2juL`45^AKT2G~VbHQ;5`Y(PUDCMQYM?8)R7Z`aoBteCQSv}bqi94 z10@8c3>5~$&h;CBSDnWm?BXIi?LlA=V8xH&!Fd{w14In99kjqxKK`IJjtC{(&8{3t zT)bBtjtGS~punPZ+CvOim%&780~-O<*gAw96Cn&Cph@Ospy0B4p1sY5v{O9+AA=hf z9@+@tMsOLQa+*Oa)Os1@6M!1G&nL$tUkAJzO~F~3j`0604R2yJ>(PqG8izmDER*ZfEW8@ZCrcjb5ll&IAHg)lqYZv zHXfa{3%?J?<)F^N1+pd}+G5>UaEDyb*Bpi{@?r=e$xsZg)>8=z?z0v-v7!JHdZd(* zaTNs7Ad;4rWTH2mII*zL~ByS1F*BbE!XewAw3(& z*mg?0@_MBKQE*?V>;V_$EeBCRrVA}vMdOu_u38e-b-y?($cC#>RkkCiH4nqyPUfg&tJmR z|1+NN@$7gqo)38j+<)NyvitA5|Cal8_eb292L5W`Ul07$z_$*R2a*F<`v2dduMT}- z=rcnHL-RxG&`|GG|IhSR`^&vQ(0{S_=lcG2@4xSRyKk^h>$}_clYM{Q|FQn>?EmfF zD?MND`Hh~>_SAZ2dp^|D=lXru=UqSOs=2bRo35VjuXTUE`v<#g-LuEFKs+h;jURz4 zZ1jSJq?ldOK$n-ocq9q@l53vx-e%lK`>hogw=#ToOLJ`L2W-LeXCg8Ifr69DE39^?})|DcPRD?(tl$W zyULyti(!1b&<7;1_`$kW=mj<;7BV_|fF^%l`ZlZBRdzuvX4Ez0k$%itprPx`%L~!M zqQgfRw4ar}MXc;;hst2;{ZVUmV93shg^bE*$bL!sZmX{TtLz!EoFN$k-hQZImAcMw z$>UhGOV%}p+P*A(r&aDMd!KVQ0z9u+1+G+WRp`fEk4KnlP2RB;(I>ITLwFE936dsnJCx}5+!0D_ zIh5$}ED0szNfwyyj5C<;e;qUUewZ?a5drMqklObliP2;_kcSP~Fp>*RATV&hrw+ ziHT#CHZuk5TK84pO3_w}UdH8-e#ERM5W8AvDMEJ;?BPGMZ07t7#0)$geR=?9t` zQXqEiwnHJh4*uSKp{ATeA-c@|ZgW|nra6Z~RMYo2=N*M+9SYGI|9#s+b6JN%A3>q- z*%At6916XNLf?H)C^X|xh%Wx`dc!J|xuqAv4A0Q@>ZWmOPAXvbv*q$yUEZ(O?&00J zw^QFkSa5Y68SATv3f@M<{C8|xwOySSn>6lC_%pw=VwKsQqB0SBO}xb~bZQZ(=H!%b zW_orqk)NaFMm`{Zp7BiYz_7k!^jp%Ie|6@D=Ud!==$;z*$pKgYYTr)}T|axv z`;P|yVsNBywfA$quAUXwm%IOu?vHkT7m9!L|6$6}`?-PT5V}`0ln-VwM2I6{1BLRb zTI^Jr>>|ObB*L2EfSFtzBR7msPVB8C=YoHWOhIckLSV*P14|CU z*uDagXh6-$A)cy(qYaE?42tx1Hg@zJ7y3Sgw#?|J&%Mt(X!H%5^} z;RU~@B6&yDw+rbPbVLVyh^%llOyVBB~b2eJVL!Z&>Zy3w#;r0$ZYyA2bIzxRAtyjEk`8k{6){&s8Lj-*L-4a(LiN zI0$(P53uTdon1P*$Y+7D39U;#`l5M#5aLn=yCja@iIpM zKUVZs8R9O;VMHCnq`Zjr%@fef6#=US!-f{=8bcDAXSsBC&!oPlk?an+tO7vMoW_G- z@@*5_HiV-Sk z0c{*^owSMvJAD5oF9M z`c&}S=rbOwBMIa)T;gp9iNoYNibmn;Je7|tn+yk|>U1{SVc&t6W3^J}ZUowI*lrOd zWLxlK9x={Qxw+#EX@m8tqo*lc`#CXKm(F1YPG6dSr20g1L8ox-kRGm`)Tu!tN-qxe zGrZ$%PKqZuvL*)V364xi<7@g)Iwtr1YsQh8@_<6-xao%>VWbUy5sVxkeW5A?fY|i$ zjpGEdfkK3!Uc{{f8GiCaTlr4ZyS+EPacS!49F4${ zJu|f4MCe7RriiTp9QG6|h$yD;*>pV4Ii~Ujp9&Nw0(E!lt7X!q>s+e~-CK~;g_JLc zhg*3SIlQ4_|MC}bZ}K~TOdE$H0)`{HhsNanN@G*LO4oj2bFZ;h-QSUOTYL4Z&`AQ_ zP+MU;#%$WvRa zt*N9hl?3xula3O(af4A`Idgj=C*O=5MBtiNvG}eYo#o=TAw_WOfk6$tH^FE$cp3$T zsHDJyHnotPF3>w>Zdc|yHfh(s2UUQgkqM7~Q?I~;^`3Z%&5 zk(V!!Nt;rl4{DJ1Fa{NZo?=j^3v&^m*ODoMcu$h@iHH8~9)gnUcy3iTDtNQV6(mHY zQ6`lNn`a@PEdc2t6CM6e84m{HXo7CYzqq&Hf9G@fGMOz-Lm@28Vc>y=Kp^5(e0)4c z=!pHwT4R$YAkvMl4L+)CARLJXKsR>kt_A)JbuG;O|4$G6ur%}&Lw+FtGv2RwzZbs$ zX9vGB_|FHo1_NjQ)0uyM=91^{xc|!iDa7jk@HhX%|A~Z8M&QW^JQ;x}Bk*Jdo{YfX z*b%4`-RBUy?(Gst{&cGmLPCIZ2Xr;41rvLftu+`Q0h5f=Qg=O%N<83 zsDjfWX3mQ-G2h5BdT9f1{0os5ffNlk>K8%PtKi8>fl}ec>C|NU4yJ0cvbIrfU}`o? zn**-II(NpRgciXPiMNskdj!;Cb zq9yTAEbUi!HvEZlc~?$UtGh$4m)GdCFLnHE2<|vog00V%w@NGisnYh!9_Tu2L$0w^ zs^Hm<6$FW@RtY7xsX_?PhY}5I zAw=9fq7MHQWtA9mRbR*Yy}*WTRcU~L!$H7LP~`9o!gCVpd~kk3jz^ArdJedDU*ipe zDPLF|VuN52(xT|WqZ))2>G&;HU+vgM^b!__a2Q7?QF3&=8_-2s~#qf{SQp642)u4YmP@*iG(D*zer=>d;xliH z560AoiBITb9j9J>QB{D0f+~HQ?OaSE1~^t+m`&hqNCNNeLj8ott|sU9>YKjY-WEJ` zMfi2MfqR44$mgZ|?FrIRHRUk>_-OE}VeKS!1BJATSaqym-VDCmea( z1*WVTa9RV{5Izl3Yrc@kB*Q+nfCP3(%v4Aa(3%|Mz-GZIN)f+G1gyPVaOlb*q|%A{ zRWSP2N95_add-8COo7dO0AX$xKtshyY($vD6{M*W#-2x9PtT>&vKk}?&jnf$ z7*krp$`G&zALg>zu?PJ03Wmvy6NDW{Jd$9cjronU{} zNMqE@G)7K*`hTh6sfb`qjcr>k>i{35}~-5UHU~abqFVPSnV>gK0Dq?L?7i$2NX;RvDB8 zuplsbLkUHn25yLQNGQw>Y|@1TC<2L7U`sk>+A0O~8N>>-3?dB7HpsY%I|E>DyL6OG zX}wx)?ADODZ4B!{Aru^H`4o0d&dxyA9j6D_t(20c6+-1uJpV3__6-nUjP*%DA+AJ zR~M&u#`G9;uAp}gMM42AJ~bYWhGu|*nn~0;FdRCQ@!D%->O3e$!6I(9sj*UoS_4{x za}=8_;;_r*mFOaIc`pRh_v1V@&>`{f$_q%w2eeTKDFjwvfh+P2gPh#AR?*3#E30+H z0j&D%?f7l707DxJg9oKi#5f5udl?uyf!GJRvOZ3;6_BDrq01KvN;-_h5Xo6(vatJj z3GGaf_DXjO7^%x5opXOqfiYQ8QZsq*C^rdhqk+QGf*Q##RZrTWt-ep@&r74e>&=<* zvLO)&Mr7lqgUU95^NsQbp8@gLJ11>h`0#Z#f!f)H+vg=Edrc7F83Da@Eti-;DMJk? z!p^%>1*hbpKhC)&*2lJME^zA+NSlIA7-g(alz=!-XPiR(6m5KS~*SI$PZ~${dNZI-IuL%%*!t1vq=!o z2egSI$V7EcXQY8#y+`W5{db!uZQK5FzSj0<&PzTdB`^nGq-CLk{zbtNu-W<_< z&L9X&JeG)2TT9PO5YOGh`bk?At9&p`T^()hDme5InB&}4Aha{(leS4c6)Syv>Jf7c zkafp7rr#aoezK3VHOI}WnQIa&{F;~<#tj#d57^#7fseKx;UuSERY>NOi!{}MR|h6$ z(($R>%fF5^EjRz-ORFax2zwjMGjU!DT^<$EKs=M5P2%zhgeQtALQ8X5ti^8s<;!2v z^N*~YR1Bv^Sbtid6pJpKZ#*&s-6|?s1Q60Hv}D1iw0>u%#VDOv6k9dCiG!5B4=h1? zbBLb7lH|W`UeD$}KV^MN%$y*D>LG|dUx_+;nF zm`yLAv}t?Y%#(0j4f@H4%v^2T0;@szr76biCl!NMgHR9e=z};I^>G$ia{$VJ3fJo} z1%ZIpoPyU*Du7UP<74NgE4FsK=(nAl4K$m5^)$`u6+e8L=3U%}eBObRo<+u)=DcTU zz4LhwS@DCH zsK;XdFgJkbKKOK%kWWRAIa{H?{rEA7r@eXU&Pm&}SNq^29oyA43wygtz-)KvwC(EE z);|oL|5-6nSw9IDR3iFALvOap&G@nNl#l8k{^sIoT5VR!q!-a!|7Bqp8KaEQX8tJS z%?f!l>P-s9kGb^>OADuw00uszfrA+OJ#5P@MTv-Fp|GA$M^AHPrvQBUKkA*IKWWpp znwd9>BP1yzk~2Qc;9DvigNi@Bi~XuSi4b zv!4U|-<#eS2d|$=o;lT z$Y(y?x{6WCd#~`r`Ej8`Y}4EG0~WTL{^l;3HQ2(M6ukN&4ds zeGwkRLU-!sYu+a$oa0tRdt-k4w_HxY5(yy?QDsXE324wSBLR)AUw4b7I32HFdRd+b ziudc*d^VTP3w`^e_qcC*gU!APvQpo`$*eFRdmfqu(iK{Bwnw)uI6Q)SGEpmEVJMVD zKqttmvp~wFq_1_5)?rm`^K_`TIoPR!1Kd}_B@NH8e-h~s?bRcTpC|>azAC{su_NB! zwj*ZeC)7YtU0Mi(Q>Md?u$7U%dVJa2c7&}azwJA7M<7iA1d6Z}dyXu$S6;J5H#?h* z6z)dDxzG}S0JP>`vVOfZmzQs*4^|O#iwItME)Tb3QgG|ag`_VM@P(w`Ieupvl_3zK z?FIHadmoMWHF1GW(gMRXDyk=|Z5!`gG9J?cShe$c6Jn4lKPYeM95H&hc%rff>U-|c z$4dZ^2n0*)A-k2@1VD)&-kOv_tBIUl6r+L&iKomhhEb0}| zV$|f>a)U_Z{Bgt(9D-m57QfxyZT~y}eUfg`AiS$pFlrr)$r%I~fQAT2hhn+5U4dh! zF8#*wM`G7Z(I}klNQ7#pX!NIr-uSKF`qfYvF3XRyr_XUSWphZpSr{n;8n}-FfwVu9 z;A7LwVc7CZ>DQ66-~!%)!_S!u4k|}_Dz6asi>xbCl^WvPQ&}zLD@;hgcKm*A@(&uF zs;r_*zOBZZ->AtiG=ZBm8ixCsjk2fcMJ(Q=b^dORg<BD$GJ#bs^=FeQiJP4@Ou=w57;0(PlgM>Fmgyhdmfj`}C!J~c4q;|(E!EcA;d-;CxL zCGtmN;M`Dy^!|TNDoI1hv#)x;WANv|^8XRf9nU5Ace)1#J`KPBQQ!a4_fqfw05<-A z+B50;m#+El-|v2{>pym_N`HutJ^l~tWz&~c8Qm5 zA9=uqsTJs%*#xK~2)i|pfr35y!FqhYCzn8vzjP_R^9z=ty zN?;#UZR>sx0OODYBX@6kt;zi34b+I#OQWKjzFLI(ebX@$k^v*FvSliwqj-VgsUkmge>uo*{mMGPH+yhIlJ&yzoQm=a13m zD!Ja)QPaIpjaXFu_i%YP#GK);v;K3^66vT4J?GCVNg2;;>9#5Bl+M$F|iV5w-u4Ta11dT_}Bx~jN>B!)DdcK zA8gjkdhjyy3+s!W&oqhqI-RK|u^<1v^!Z~5v$efSWPp|JZRb-JW8;ExA<{hCp}`C% z3$q!@5jIg&GLJGY*vm=ZE}rqn!3o$u&y5(@k*9NYeLIJE2vt3{5V`Q;Etm-xqWE^bWc3o0UBtoQUFM#Gio6y1mB4E!>7m6_V%@$$c z3(Szw8)4po8J=xZ`q2~hq`bRYt@|(~{+4#|5ESI`Bjq5%Um|Q~A4jL(FTA_%6bo6X z9bvNB6n&`$UmUO3wx6~AAUQWXD^I24DcH2}9s~114n2m|BACreKXRfz=;kGEr*CV< z-}VzSR3eHZ6b@-D`vB`nyFO!0!!l%n@)gKskj>3-**f@$#Zr3a`$BSRu2`J8L%ht` z`V^IdinNK0^qCWNX1lUltCCT~a7g1zzu21X8$&?!2S!*m#@6=m^2;iM>)}mq7*_$% z608cs!Op3h!Q2?vP3x2Sq7se5-Y}&b?zL1ho&(Vf7_i6H`3W9eLAZCq=}13(vW`98 zs^8kSZq;uJ?+0ux0+TCyu|X%@$fPxG*A`{RD6_ZZw+ZDZsCgvTix>RB(67-ueq!kV8v4tjKOOo{ zLw_*zwV~f0`Zdp=dEWAP+}|?r?*`uJ|KIwz`jWk$>V3B7ANPb@zwTP^{+;fJU7zWC zpOi6;0BrK+B`L=G@wQG5z}W5{@FE9~+YK9o*PC39<|dZOAhT@5Qut?c0g9z~=d++P z#lLh%wIA?^qySZRE+ErK<(s$$z~iW&@*T#;S>FMVBMh=(;CRTKwOgt+mMhz^@znkN z>tF!iZj8UNyRo}k?_pgxE=tNLkDY@0%4Ryx=W*}Tt}=ak?qz=i0d*IQkar!L;Q*>-9JeEnwh3~sgMY=*KWsaY(q#UkiK z5k@mbfuDO6F-tL0w3W<;K@rORIz-kFJ(XDb+XV>=f}GjDhUGLb%7J}hY>VK0M5iZK zLkQvt7lPxFz19#O9B)?RDA(RxNNBF-^{W@9E0a#mrLswwER{?lW#}8T{+us4MJ)7k zbUYOIBhNszfJ6%|+d~Tq^?P=%T$FBk z9+boQ3O_Bc;@{Y-2POPh4s5@Au)ccZ!TT>tmk*tm@pLi^i+wnNybJSk*cU&j)aBCZ zDqtVADlBdAm-*$5!&-Gro>r4cj=6@2?rE$!{03=_%54**iYKj`xt_ZyJy-SK^EPiY zbA3T`Cw4i_hfpg@leDo@ZWt|MMQ0}C=3>@cZnh|nKgggP<$I<@9;AB92!^GTb9{Gm z_vPKn+DkzUoqPY%MQOC^)cb4!f#q=BB;o;h$peWJ?=;?Vh^j5uc^WAqoF?jmV|HIZ zd~f9@sbMA$V>6e%+t&8#GEUz@JnPFwZ~F+Wu?O0)19>u6FgfcAb4i^>oO-QCpke46 z<((ZHoPW=^f)ASAH@fb}Bfz&Ez3+=L)@~%(XQH#_PuA&=&Ez0O`X7O6w9LZ zE~eYWDV(;wul%?-Rm!)XMzi%!%?4(59e`A0DF3)hb|yF@`QF4xOE@p-0z&)v1VwAN zUucK|7c|=Hg^QBkc7+PFiyIWRQa&h(B39bEY*Rl_%6XWPQA%iV?J4L8S*QMj+hqnB z8FU>1UxFataQwF@c%&b+jjAFZZ7$fKF|0UMtc@e|D?r1_+X+8*SOvur?S?xHs>1cl z}KqpwGC{@p|heE=_(K59J?(xV;1KGZ!dT}ckJXW|3TLmh%5#ML2s1rtq&UPlx7LO(*<3X-L?R@%jt4iyJ*LGg+` zz7~fMj^aK3w^SuNQ^|u!A*V`Y)U!S~5Km33+Aq^GIe(mi3 z*$dwP&YK?m`N2D9{==C{`}<#-=>5&!a?fA%{JowJx_;F4V)xyy-{=}dwMnkRLIbq! zSaY0PHNa!3uzyWjo@P^IpV%*+*e_r`d1Al7<6jeRo+tJT9hm5#pFFW&TzK;SfAao+ z^8P0@xdljk^8V)>SZ#{3Dr(A4-v5RR0MGp=?|;M3Vt?TEukWUjz5kz*RB7np*@tJZdp|z-JA*xE?s$^!e=@-O=lk{~ zwRf*8(lg=8b&sJa{v36Ohov~&((Xmwrc!M>`6uHEu&6G=&5j5M1RhM?1(a+?x$7%x zW`KkBD;anIg4&Rs<0Sl0$iD+#LP59-GB#(P7Cq zi9xud55k+xLAa5P0sR{FrSjPctvKnQ0k|uj&gJK_02M`NxaUTtv5}+MOzxz;4+0*j zgtRarOCHtxy?7(8#8cz@<&|AHK@0)1)MghcaTy zAeVeoJm_Jb4OP`d`Svb0q;oboo25zd+mk&5eSx$PDlwr$0C3Ulv;&? zy!3RTrkR&#;M~SQR^S{qe88$6Abrx&*hM68G~kRj2qoOK z012}0-y|r22{I8=$H&u2-)*cH=r%^r3h+xI`m70r=y^{?2ig_%Je0oyOOwXp{c_bbMdIV6-EYMoX2!dlzIvgQDTr{2I>=^TwILrmbG5ib*A?-OU!&OfesyEy%fiIx!tD z0M#?gySpeaWQ*M1`RH>%0BzQ=bnL+ zYGq%dQmVrmoTz|L4@A&KN=K}l0d6L46-T)AK3OOUPYhuIFf72p(oUi=S_IcM_t64q zLwxJhQxAJ90t+!tkK4u~v5ru0HXFJdTTl=St^@h*re^_n1I;c`2?w&MZ!Q&5Pc~X@ zQQ%f1e(Osi{#yw|L!HN}Sev`En7VDQpSzTh*C3NNV`E2-lOXnfF53-+QuZ4!C z>n~ZS=UdilDP|VJz~4+$JU6z-TuzQ>b8;R`19W7bZeMT)Nw78%_Ulm_W2Jg96}uHO zJ7#7Y&Ka48zpx=3A`ooNAOykoy}m89Xe1I*Vx6aYVgGP1cz44b5lCs! zvJ+s4MAh-+IPURPX#}Tv%o$l;-7nX2@tJpi+8CrT;&yj7w#uHPYbuV8Jscg=#-;L% zHCD6pIdF@Gh-=%N_r0fd@l8Dj|C6B$Wg$2)n$)G_-Ae;$$KlBT&`7*_pZ;hKE7L5dh(Y9#| z>hDTJT_~2f5lU9#z(~5|@i^6ae<|pE6$&9_r+D;FViSbu3nj3PoZ|SxxDvhWrXLz& z(#xK0pRvMpC=>`Lkp5kd25iUT1>>j~Aodf_T)je7udJ;a@qx~|QBZ_Qdf3AFCFwd- zu)2&H6mYx6Tp_F~5j9nq%=2{ida?;@UH~)A#P(QsEjbZ%UB*m|fQ^D{G3r~KNKMSj z=>tmKz9#2NCnhcBv>h>=pUx+FQ1@CGlQ_sEA22O%imq&P(}3E&4IBW-g%0C|``-!0 zOQ38rPuM`n?F&hCruVSK7?#1}FdM}(;Mh28Z&036kR&wol!DfvbCOaNqo_5G@ZLT8j?s4qVtG(p@ge=Fn|{u zJEpx-@nwF2z4al-k#S$CdRsHNTGATHYnlK^J=Lo@GRXgWx{<*L`N$}t$Bs<%35{pf zKobzCt=s_EkAQ#5(yIrn>#rUJfD2p+R$jo5G++T&KZGAgYBM@$Ft&Dsxkdn80p_dy ze4xc?fy^HeDp*144l@+$-Faz)Hdqq9|1U}f*V+I5?8m(8gAdQV?fH!R^X{dA-y7)Z zSHbxI41!JntY_8rRo7hi7rR4U|5Mjp>F1;ZHw8HVRapR*4fqk`DS)%YTrEu+$2?yC zn_$LY1MnDW9_sh^O0_Z))a;aY<@HLVZRIr8^ohm8NU*wKsOj%x!}G(^2)pdmn4P^* zGamULBNd%1=LQ)Gy?*40a;;Wrl;s2-{y2C6sJ{tzgl3=tK7;dkJnV?{ZpwD=U}fW8 z`o1CCGp`meO4s7z^qRvUurPA1hUaRDkV2*40hpRom0&#HHU?1tv znl^yW(!>|H7j{>7OkJ;s4dxyp4xs9^xRyCo9pb1~kcY zy61Fptb5&Dgg9difF(ABj*hdWp&xY=2cS#r4kg@ibRss(3&v!&^O!Udmj-;_Jm)By zro)&}yjPoBIr%+ zN4TLk37bkGsH(0vIZJfXJ+&I!zk8>lTEg4{gb(+mvoO%&NK?j01s3pMQx+aa*J(#R zb_q96zQa;vGroDp6b3gQo14a+^X4$SZe85wrkXljsa4kGcrHPC=fy}3^EZ13;0P$e z2CGwHC3q^no4Mq?9DjiG5NTpYpy7exuKQb!+1Pu z(RQ;}MAyW0^{$)UorJl9dG#(CN~&|0;?$>79W*a1TZc_lFM-nlg;^ZfyrnM8sQrO6iNz?a61sNoN9F&6tBU<~qHz zL28Gg*2?ocV09}V?m_{fb%OwiGX1MtHqtQm%k&|=K$5

e=1PUQG>4h1Z^CA?s;j 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 Date: Sat, 18 May 2019 23:52:22 +1000 Subject: [PATCH 22/23] Hopefully fix the thingy --- InvenTree/stock/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 2e48ea167f..04e814707e 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -189,7 +189,7 @@ class StockItem(models.Model): part = models.ForeignKey('part.Part', on_delete=models.CASCADE, related_name='locations', help_text='Base part') - supplier_part = models.ForeignKey(SupplierPart, blank=True, null=True, on_delete=models.SET_NULL, + supplier_part = models.ForeignKey('company.SupplierPart', blank=True, null=True, on_delete=models.SET_NULL, help_text='Select a matching supplier part for this stock item') location = models.ForeignKey(StockLocation, on_delete=models.DO_NOTHING, From 9ddedc6915f69aa0988ab8d6dce0b4ec64687b9c Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 19 May 2019 00:11:41 +1000 Subject: [PATCH 23/23] Restart migrations - Easier to delete all the migrations and start again :'( --- InvenTree/build/migrations/0001_initial.py | 36 +++++--- .../migrations/0002_auto_20190412_2030.py | 25 ------ .../migrations/0003_builditemallocation.py | 25 ------ InvenTree/build/migrations/0004_build_url.py | 18 ---- .../migrations/0005_auto_20190429_2229.py | 18 ---- .../migrations/0006_auto_20190429_2233.py | 18 ---- .../migrations/0007_auto_20190429_2255.py | 18 ---- .../migrations/0008_auto_20190501_2344.py | 25 ------ .../migrations/0009_build_completed_by.py | 21 ----- .../migrations/0010_auto_20190505_2233.py | 19 ---- .../migrations/0011_auto_20190508_0748.py | 19 ---- .../migrations/0012_auto_20190508_2332.py | 30 ------- .../build/migrations/0013_build_take_from.py | 20 ----- .../migrations/0014_auto_20190518_1543.py | 19 ---- InvenTree/company/migrations/0001_initial.py | 61 +++++++++++-- .../migrations/0002_auto_20180422_1201.py | 25 ------ .../migrations/0002_auto_20190519_0004.py} | 35 ++++---- .../migrations/0003_auto_20180423_1117.py | 25 ------ .../company/migrations/0004_company_url.py | 20 ----- InvenTree/company/migrations/0005_contact.py | 27 ------ .../migrations/0006_auto_20190508_2332.py | 43 ---------- .../0007_supplierpart_supplierpricebreak.py | 52 ----------- .../migrations/0008_auto_20190518_2120.py | 19 ---- InvenTree/part/migrations/0001_initial.py | 86 +++++-------------- .../migrations/0002_auto_20190519_0004.py | 77 +++++++++++++++++ .../migrations/0002_part_default_location.py | 22 ----- .../migrations/0003_auto_20190412_2030.py | 19 ---- .../part/migrations/0004_bomitem_note.py | 18 ---- .../part/migrations/0005_part_consumable.py | 18 ---- .../migrations/0006_auto_20190416_2354.py | 30 ------- .../migrations/0007_auto_20190417_0007.py | 54 ------------ .../migrations/0008_auto_20190417_0013.py | 19 ---- .../migrations/0009_auto_20190417_0019.py | 19 ---- .../migrations/0010_auto_20190417_0045.py | 24 ------ .../migrations/0011_auto_20190428_0841.py | 19 ---- InvenTree/part/migrations/0012_part_active.py | 23 ----- .../migrations/0013_auto_20190429_2229.py | 18 ---- .../migrations/0014_auto_20190502_2039.py | 18 ---- .../0015_partcategory_default_location.py | 20 ----- InvenTree/part/migrations/0016_partstar.py | 24 ------ .../migrations/0017_auto_20190505_0848.py | 19 ---- .../migrations/0018_auto_20190505_2231.py | 24 ------ .../migrations/0020_auto_20190510_2022.py | 27 ------ .../migrations/0021_auto_20190510_2220.py | 19 ---- .../migrations/0022_auto_20190512_1246.py | 31 ------- .../part/migrations/0023_part_keywords.py | 18 ---- .../0024_partcategory_default_keywords.py | 18 ---- .../migrations/0025_auto_20190515_0012.py | 46 ---------- .../0026_remove_supplierpart_single_price.py | 17 ---- .../migrations/0027_auto_20190518_1620.py | 18 ---- .../migrations/0028_auto_20190518_1627.py | 19 ---- .../migrations/0029_auto_20190518_1632.py | 19 ---- .../migrations/0030_auto_20190518_1641.py | 19 ---- .../migrations/0031_auto_20190518_1650.py | 19 ---- .../migrations/0032_auto_20190518_1759.py | 34 -------- InvenTree/stock/migrations/0001_initial.py | 57 ++++++------ .../migrations/0002_auto_20180430_1218.py | 21 ----- .../migrations/0003_auto_20180510_1042.py | 20 ----- .../migrations/0004_auto_20190412_2030.py | 19 ---- .../0005_stockitemtracking_quantity.py | 19 ---- .../stock/migrations/0006_stockitem_uuid.py | 19 ---- .../migrations/0007_auto_20190417_1812.py | 18 ---- .../migrations/0008_auto_20190417_1819.py | 20 ----- .../migrations/0009_auto_20190428_0841.py | 19 ---- .../migrations/0010_auto_20190501_2344.py | 18 ---- .../migrations/0011_auto_20190502_0041.py | 19 ---- .../migrations/0012_auto_20190502_0058.py | 19 ---- .../migrations/0013_remove_stockitem_uuid.py | 17 ---- .../migrations/0014_auto_20190508_2332.py | 19 ---- .../0015_stockitem_delete_on_deplete.py | 18 ---- .../migrations/0016_auto_20190512_2119.py | 18 ---- .../migrations/0017_auto_20190518_1759.py | 19 ---- InvenTree/stock/models.py | 1 - 73 files changed, 224 insertions(+), 1609 deletions(-) delete mode 100644 InvenTree/build/migrations/0002_auto_20190412_2030.py delete mode 100644 InvenTree/build/migrations/0003_builditemallocation.py delete mode 100644 InvenTree/build/migrations/0004_build_url.py delete mode 100644 InvenTree/build/migrations/0005_auto_20190429_2229.py delete mode 100644 InvenTree/build/migrations/0006_auto_20190429_2233.py delete mode 100644 InvenTree/build/migrations/0007_auto_20190429_2255.py delete mode 100644 InvenTree/build/migrations/0008_auto_20190501_2344.py delete mode 100644 InvenTree/build/migrations/0009_build_completed_by.py delete mode 100644 InvenTree/build/migrations/0010_auto_20190505_2233.py delete mode 100644 InvenTree/build/migrations/0011_auto_20190508_0748.py delete mode 100644 InvenTree/build/migrations/0012_auto_20190508_2332.py delete mode 100644 InvenTree/build/migrations/0013_build_take_from.py delete mode 100644 InvenTree/build/migrations/0014_auto_20190518_1543.py delete mode 100644 InvenTree/company/migrations/0002_auto_20180422_1201.py rename InvenTree/{part/migrations/0019_auto_20190508_2332.py => company/migrations/0002_auto_20190519_0004.py} (51%) delete mode 100644 InvenTree/company/migrations/0003_auto_20180423_1117.py delete mode 100644 InvenTree/company/migrations/0004_company_url.py delete mode 100644 InvenTree/company/migrations/0005_contact.py delete mode 100644 InvenTree/company/migrations/0006_auto_20190508_2332.py delete mode 100644 InvenTree/company/migrations/0007_supplierpart_supplierpricebreak.py delete mode 100644 InvenTree/company/migrations/0008_auto_20190518_2120.py create mode 100644 InvenTree/part/migrations/0002_auto_20190519_0004.py delete mode 100644 InvenTree/part/migrations/0002_part_default_location.py delete mode 100644 InvenTree/part/migrations/0003_auto_20190412_2030.py delete mode 100644 InvenTree/part/migrations/0004_bomitem_note.py delete mode 100644 InvenTree/part/migrations/0005_part_consumable.py delete mode 100644 InvenTree/part/migrations/0006_auto_20190416_2354.py delete mode 100644 InvenTree/part/migrations/0007_auto_20190417_0007.py delete mode 100644 InvenTree/part/migrations/0008_auto_20190417_0013.py delete mode 100644 InvenTree/part/migrations/0009_auto_20190417_0019.py delete mode 100644 InvenTree/part/migrations/0010_auto_20190417_0045.py delete mode 100644 InvenTree/part/migrations/0011_auto_20190428_0841.py delete mode 100644 InvenTree/part/migrations/0012_part_active.py delete mode 100644 InvenTree/part/migrations/0013_auto_20190429_2229.py delete mode 100644 InvenTree/part/migrations/0014_auto_20190502_2039.py delete mode 100644 InvenTree/part/migrations/0015_partcategory_default_location.py delete mode 100644 InvenTree/part/migrations/0016_partstar.py delete mode 100644 InvenTree/part/migrations/0017_auto_20190505_0848.py delete mode 100644 InvenTree/part/migrations/0018_auto_20190505_2231.py delete mode 100644 InvenTree/part/migrations/0020_auto_20190510_2022.py delete mode 100644 InvenTree/part/migrations/0021_auto_20190510_2220.py delete mode 100644 InvenTree/part/migrations/0022_auto_20190512_1246.py delete mode 100644 InvenTree/part/migrations/0023_part_keywords.py delete mode 100644 InvenTree/part/migrations/0024_partcategory_default_keywords.py delete mode 100644 InvenTree/part/migrations/0025_auto_20190515_0012.py delete mode 100644 InvenTree/part/migrations/0026_remove_supplierpart_single_price.py delete mode 100644 InvenTree/part/migrations/0027_auto_20190518_1620.py delete mode 100644 InvenTree/part/migrations/0028_auto_20190518_1627.py delete mode 100644 InvenTree/part/migrations/0029_auto_20190518_1632.py delete mode 100644 InvenTree/part/migrations/0030_auto_20190518_1641.py delete mode 100644 InvenTree/part/migrations/0031_auto_20190518_1650.py delete mode 100644 InvenTree/part/migrations/0032_auto_20190518_1759.py delete mode 100644 InvenTree/stock/migrations/0002_auto_20180430_1218.py delete mode 100644 InvenTree/stock/migrations/0003_auto_20180510_1042.py delete mode 100644 InvenTree/stock/migrations/0004_auto_20190412_2030.py delete mode 100644 InvenTree/stock/migrations/0005_stockitemtracking_quantity.py delete mode 100644 InvenTree/stock/migrations/0006_stockitem_uuid.py delete mode 100644 InvenTree/stock/migrations/0007_auto_20190417_1812.py delete mode 100644 InvenTree/stock/migrations/0008_auto_20190417_1819.py delete mode 100644 InvenTree/stock/migrations/0009_auto_20190428_0841.py delete mode 100644 InvenTree/stock/migrations/0010_auto_20190501_2344.py delete mode 100644 InvenTree/stock/migrations/0011_auto_20190502_0041.py delete mode 100644 InvenTree/stock/migrations/0012_auto_20190502_0058.py delete mode 100644 InvenTree/stock/migrations/0013_remove_stockitem_uuid.py delete mode 100644 InvenTree/stock/migrations/0014_auto_20190508_2332.py delete mode 100644 InvenTree/stock/migrations/0015_stockitem_delete_on_deplete.py delete mode 100644 InvenTree/stock/migrations/0016_auto_20190512_2119.py delete mode 100644 InvenTree/stock/migrations/0017_auto_20190518_1759.py diff --git a/InvenTree/build/migrations/0001_initial.py b/InvenTree/build/migrations/0001_initial.py index 26e835b978..aa7356389a 100644 --- a/InvenTree/build/migrations/0001_initial.py +++ b/InvenTree/build/migrations/0001_initial.py @@ -1,7 +1,6 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.12 on 2018-04-22 11:53 -from __future__ import unicode_literals +# Generated by Django 2.2 on 2019-05-18 14:04 +from django.conf import settings import django.core.validators from django.db import migrations, models import django.db.models.deletion @@ -12,7 +11,9 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('part', '0001_initial'), + ('part', '0002_auto_20190519_0004'), + ('stock', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ @@ -20,14 +21,29 @@ class Migration(migrations.Migration): name='Build', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('batch', models.CharField(blank=True, help_text='Batch code for this build output', max_length=100, null=True)), - ('status', models.PositiveIntegerField(choices=[(40, 'Complete'), (10, 'Pending'), (20, 'Holding'), (30, 'Cancelled')], default=10, validators=[django.core.validators.MinValueValidator(0)])), - ('creation_date', models.DateField(auto_now=True)), - ('completion_date', models.DateField(blank=True, null=True)), ('title', models.CharField(help_text='Brief description of the build', max_length=100)), ('quantity', models.PositiveIntegerField(default=1, help_text='Number of parts to build', validators=[django.core.validators.MinValueValidator(1)])), - ('notes', models.TextField(blank=True)), - ('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='builds', to='part.Part')), + ('status', models.PositiveIntegerField(choices=[(10, 'Pending'), (20, 'Allocated'), (30, 'Cancelled'), (40, 'Complete')], default=10, help_text='Build status', validators=[django.core.validators.MinValueValidator(0)])), + ('batch', models.CharField(blank=True, help_text='Batch code for this build output', max_length=100, null=True)), + ('creation_date', models.DateField(auto_now=True)), + ('completion_date', models.DateField(blank=True, null=True)), + ('URL', models.URLField(blank=True, help_text='Link to external URL')), + ('notes', models.TextField(blank=True, help_text='Extra build notes')), + ('completed_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='builds_completed', to=settings.AUTH_USER_MODEL)), + ('part', models.ForeignKey(help_text='Select part to build', limit_choices_to={'active': True, 'buildable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='builds', to='part.Part')), + ('take_from', models.ForeignKey(blank=True, help_text='Select location to take stock from for this build (leave blank to take from any stock location)', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sourcing_builds', to='stock.StockLocation')), ], ), + migrations.CreateModel( + name='BuildItem', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('quantity', models.PositiveIntegerField(default=1, help_text='Stock quantity to allocate to build', validators=[django.core.validators.MinValueValidator(1)])), + ('build', models.ForeignKey(help_text='Build to allocate parts', on_delete=django.db.models.deletion.CASCADE, related_name='allocated_stock', to='build.Build')), + ('stock_item', models.ForeignKey(help_text='Stock Item to allocate to build', on_delete=django.db.models.deletion.CASCADE, related_name='allocations', to='stock.StockItem')), + ], + options={ + 'unique_together': {('build', 'stock_item')}, + }, + ), ] diff --git a/InvenTree/build/migrations/0002_auto_20190412_2030.py b/InvenTree/build/migrations/0002_auto_20190412_2030.py deleted file mode 100644 index eca2bf5adb..0000000000 --- a/InvenTree/build/migrations/0002_auto_20190412_2030.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 2.2 on 2019-04-12 10:30 - -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='build', - name='part', - field=models.ForeignKey(limit_choices_to={'buildable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='builds', to='part.Part'), - ), - migrations.AlterField( - model_name='build', - name='status', - field=models.PositiveIntegerField(choices=[(10, 'Pending'), (20, 'Holding'), (30, 'Cancelled'), (40, 'Complete')], default=10, validators=[django.core.validators.MinValueValidator(0)]), - ), - ] diff --git a/InvenTree/build/migrations/0003_builditemallocation.py b/InvenTree/build/migrations/0003_builditemallocation.py deleted file mode 100644 index add13c7ac1..0000000000 --- a/InvenTree/build/migrations/0003_builditemallocation.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 2.2 on 2019-04-29 12:14 - -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0009_auto_20190428_0841'), - ('build', '0002_auto_20190412_2030'), - ] - - operations = [ - migrations.CreateModel( - name='BuildItemAllocation', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('quantity', models.PositiveIntegerField(default=1, help_text='Stock quantity to allocate to build', validators=[django.core.validators.MinValueValidator(1)])), - ('build', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='allocated_stock', to='build.Build')), - ('stock', models.ForeignKey(help_text='Stock Item to allocate to build', on_delete=django.db.models.deletion.CASCADE, related_name='allocations', to='stock.StockItem')), - ], - ), - ] diff --git a/InvenTree/build/migrations/0004_build_url.py b/InvenTree/build/migrations/0004_build_url.py deleted file mode 100644 index 187ac938d1..0000000000 --- a/InvenTree/build/migrations/0004_build_url.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2 on 2019-04-29 12:18 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0003_builditemallocation'), - ] - - operations = [ - migrations.AddField( - model_name='build', - name='URL', - field=models.URLField(blank=True, help_text='Link to external URL'), - ), - ] diff --git a/InvenTree/build/migrations/0005_auto_20190429_2229.py b/InvenTree/build/migrations/0005_auto_20190429_2229.py deleted file mode 100644 index 73ebca7e1b..0000000000 --- a/InvenTree/build/migrations/0005_auto_20190429_2229.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2 on 2019-04-29 12:29 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0009_auto_20190428_0841'), - ('build', '0004_build_url'), - ] - - operations = [ - migrations.RenameModel( - old_name='BuildItemAllocation', - new_name='BuildItem', - ), - ] diff --git a/InvenTree/build/migrations/0006_auto_20190429_2233.py b/InvenTree/build/migrations/0006_auto_20190429_2233.py deleted file mode 100644 index 5d03e13b75..0000000000 --- a/InvenTree/build/migrations/0006_auto_20190429_2233.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2 on 2019-04-29 12:33 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0005_auto_20190429_2229'), - ] - - operations = [ - migrations.RenameField( - model_name='builditem', - old_name='stock', - new_name='stock_item', - ), - ] diff --git a/InvenTree/build/migrations/0007_auto_20190429_2255.py b/InvenTree/build/migrations/0007_auto_20190429_2255.py deleted file mode 100644 index 8a01606b84..0000000000 --- a/InvenTree/build/migrations/0007_auto_20190429_2255.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2 on 2019-04-29 12:55 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0009_auto_20190428_0841'), - ('build', '0006_auto_20190429_2233'), - ] - - operations = [ - migrations.AlterUniqueTogether( - name='builditem', - unique_together={('build', 'stock_item')}, - ), - ] diff --git a/InvenTree/build/migrations/0008_auto_20190501_2344.py b/InvenTree/build/migrations/0008_auto_20190501_2344.py deleted file mode 100644 index febdd2d1b1..0000000000 --- a/InvenTree/build/migrations/0008_auto_20190501_2344.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 2.2 on 2019-05-01 13:44 - -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0007_auto_20190429_2255'), - ] - - operations = [ - migrations.AlterField( - model_name='build', - name='status', - field=models.PositiveIntegerField(choices=[(10, 'Pending'), (30, 'Cancelled'), (40, 'Complete')], default=10, validators=[django.core.validators.MinValueValidator(0)]), - ), - migrations.AlterField( - model_name='builditem', - name='build', - field=models.ForeignKey(help_text='Build to allocate parts', on_delete=django.db.models.deletion.CASCADE, related_name='allocated_stock', to='build.Build'), - ), - ] diff --git a/InvenTree/build/migrations/0009_build_completed_by.py b/InvenTree/build/migrations/0009_build_completed_by.py deleted file mode 100644 index dc6c05a3f7..0000000000 --- a/InvenTree/build/migrations/0009_build_completed_by.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 2.2 on 2019-05-02 21:26 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('build', '0008_auto_20190501_2344'), - ] - - operations = [ - migrations.AddField( - model_name='build', - name='completed_by', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='builds_completed', to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/InvenTree/build/migrations/0010_auto_20190505_2233.py b/InvenTree/build/migrations/0010_auto_20190505_2233.py deleted file mode 100644 index 5be6c393c9..0000000000 --- a/InvenTree/build/migrations/0010_auto_20190505_2233.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-05-05 12:33 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0009_build_completed_by'), - ] - - operations = [ - migrations.AlterField( - model_name='build', - name='part', - field=models.ForeignKey(limit_choices_to={'active': True, 'buildable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='builds', to='part.Part'), - ), - ] diff --git a/InvenTree/build/migrations/0011_auto_20190508_0748.py b/InvenTree/build/migrations/0011_auto_20190508_0748.py deleted file mode 100644 index e4c0ec5fa7..0000000000 --- a/InvenTree/build/migrations/0011_auto_20190508_0748.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-05-07 21:48 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0010_auto_20190505_2233'), - ] - - operations = [ - migrations.AlterField( - model_name='build', - name='status', - field=models.PositiveIntegerField(choices=[(10, 'Pending'), (20, 'Allocated'), (30, 'Cancelled'), (40, 'Complete')], default=10, validators=[django.core.validators.MinValueValidator(0)]), - ), - ] diff --git a/InvenTree/build/migrations/0012_auto_20190508_2332.py b/InvenTree/build/migrations/0012_auto_20190508_2332.py deleted file mode 100644 index 099fce3fe9..0000000000 --- a/InvenTree/build/migrations/0012_auto_20190508_2332.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 2.2 on 2019-05-08 13:32 - -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0011_auto_20190508_0748'), - ] - - operations = [ - migrations.AlterField( - model_name='build', - name='notes', - field=models.TextField(blank=True, help_text='Extra build notes'), - ), - migrations.AlterField( - model_name='build', - name='part', - field=models.ForeignKey(help_text='Select part to build', limit_choices_to={'active': True, 'buildable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='builds', to='part.Part'), - ), - migrations.AlterField( - model_name='build', - name='status', - field=models.PositiveIntegerField(choices=[(10, 'Pending'), (20, 'Allocated'), (30, 'Cancelled'), (40, 'Complete')], default=10, help_text='Build status', validators=[django.core.validators.MinValueValidator(0)]), - ), - ] diff --git a/InvenTree/build/migrations/0013_build_take_from.py b/InvenTree/build/migrations/0013_build_take_from.py deleted file mode 100644 index 9945da2be1..0000000000 --- a/InvenTree/build/migrations/0013_build_take_from.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 2.2 on 2019-05-10 08:50 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0015_stockitem_delete_on_deplete'), - ('build', '0012_auto_20190508_2332'), - ] - - operations = [ - migrations.AddField( - model_name='build', - name='take_from', - field=models.ForeignKey(blank=True, help_text='Select location to take stock from for this build (leave blank to take from any stock location', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sourcing_builds', to='stock.StockLocation'), - ), - ] diff --git a/InvenTree/build/migrations/0014_auto_20190518_1543.py b/InvenTree/build/migrations/0014_auto_20190518_1543.py deleted file mode 100644 index 9fbd095445..0000000000 --- a/InvenTree/build/migrations/0014_auto_20190518_1543.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-05-18 05:43 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0013_build_take_from'), - ] - - operations = [ - migrations.AlterField( - model_name='build', - name='take_from', - field=models.ForeignKey(blank=True, help_text='Select location to take stock from for this build (leave blank to take from any stock location)', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sourcing_builds', to='stock.StockLocation'), - ), - ] diff --git a/InvenTree/company/migrations/0001_initial.py b/InvenTree/company/migrations/0001_initial.py index 791011304c..b4da1df89e 100644 --- a/InvenTree/company/migrations/0001_initial.py +++ b/InvenTree/company/migrations/0001_initial.py @@ -1,9 +1,9 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.12 on 2018-04-22 11:53 -from __future__ import unicode_literals +# Generated by Django 2.2 on 2019-05-18 14:04 import company.models +import django.core.validators from django.db import migrations, models +import django.db.models.deletion class Migration(migrations.Migration): @@ -18,15 +18,60 @@ class Migration(migrations.Migration): name='Company', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(help_text='Company naem', max_length=100, unique=True)), - ('description', models.CharField(max_length=500)), + ('name', models.CharField(help_text='Company name', max_length=100, unique=True)), + ('description', models.CharField(help_text='Description of the company', max_length=500)), ('website', models.URLField(blank=True, help_text='Company website URL')), ('address', models.CharField(blank=True, help_text='Company address', max_length=200)), - ('phone', models.CharField(blank=True, max_length=50)), - ('email', models.EmailField(blank=True, max_length=254)), - ('contact', models.CharField(blank=True, max_length=100)), + ('phone', models.CharField(blank=True, help_text='Contact phone number', max_length=50)), + ('email', models.EmailField(blank=True, help_text='Contact email address', max_length=254)), + ('contact', models.CharField(blank=True, help_text='Point of contact', max_length=100)), + ('URL', models.URLField(blank=True, help_text='Link to external company information')), ('image', models.ImageField(blank=True, max_length=255, null=True, upload_to=company.models.rename_company_image)), ('notes', models.TextField(blank=True)), + ('is_customer', models.BooleanField(default=False, help_text='Do you sell items to this company?')), + ('is_supplier', models.BooleanField(default=True, help_text='Do you purchase items from this company?')), ], ), + migrations.CreateModel( + name='Contact', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('phone', models.CharField(blank=True, max_length=100)), + ('email', models.EmailField(blank=True, max_length=254)), + ('role', models.CharField(blank=True, max_length=100)), + ], + ), + migrations.CreateModel( + name='SupplierPart', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('SKU', models.CharField(help_text='Supplier stock keeping unit', max_length=100)), + ('manufacturer', models.CharField(blank=True, help_text='Manufacturer', max_length=100)), + ('MPN', models.CharField(blank=True, help_text='Manufacturer part number', max_length=100)), + ('URL', models.URLField(blank=True, help_text='URL for external supplier part link')), + ('description', models.CharField(blank=True, help_text='Supplier part description', max_length=250)), + ('note', models.CharField(blank=True, help_text='Notes', max_length=100)), + ('base_cost', models.DecimalField(decimal_places=3, default=0, help_text='Minimum charge (e.g. stocking fee)', max_digits=10, validators=[django.core.validators.MinValueValidator(0)])), + ('packaging', models.CharField(blank=True, help_text='Part packaging', max_length=50)), + ('multiple', models.PositiveIntegerField(default=1, help_text='Order multiple', validators=[django.core.validators.MinValueValidator(1)])), + ('minimum', models.PositiveIntegerField(default=1, help_text='Minimum order quantity (MOQ)', validators=[django.core.validators.MinValueValidator(1)])), + ('lead_time', models.DurationField(blank=True, null=True)), + ], + options={ + 'db_table': 'part_supplierpart', + }, + ), + migrations.CreateModel( + name='SupplierPriceBreak', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('quantity', models.PositiveIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1)])), + ('cost', models.DecimalField(decimal_places=5, max_digits=10, validators=[django.core.validators.MinValueValidator(0)])), + ('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pricebreaks', to='company.SupplierPart')), + ], + options={ + 'db_table': 'part_supplierpricebreak', + }, + ), ] diff --git a/InvenTree/company/migrations/0002_auto_20180422_1201.py b/InvenTree/company/migrations/0002_auto_20180422_1201.py deleted file mode 100644 index 38578a58fe..0000000000 --- a/InvenTree/company/migrations/0002_auto_20180422_1201.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.12 on 2018-04-22 12:01 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='company', - name='is_customer', - field=models.BooleanField(default=False), - ), - migrations.AddField( - model_name='company', - name='is_supplier', - field=models.BooleanField(default=False), - ), - ] diff --git a/InvenTree/part/migrations/0019_auto_20190508_2332.py b/InvenTree/company/migrations/0002_auto_20190519_0004.py similarity index 51% rename from InvenTree/part/migrations/0019_auto_20190508_2332.py rename to InvenTree/company/migrations/0002_auto_20190519_0004.py index b61bbee82b..c34c315d48 100644 --- a/InvenTree/part/migrations/0019_auto_20190508_2332.py +++ b/InvenTree/company/migrations/0002_auto_20190519_0004.py @@ -1,35 +1,40 @@ -# Generated by Django 2.2 on 2019-05-08 13:32 +# Generated by Django 2.2 on 2019-05-18 14:04 from django.db import migrations, models import django.db.models.deletion -import part.models class Migration(migrations.Migration): + initial = True + dependencies = [ - ('part', '0018_auto_20190505_2231'), + ('company', '0001_initial'), + ('part', '0001_initial'), ] operations = [ - migrations.AlterField( - model_name='part', - name='units', - field=models.CharField(blank=True, default='pcs', help_text='Stock keeping units for this part', max_length=20), - ), - migrations.AlterField( - model_name='partattachment', - name='attachment', - field=models.FileField(blank=True, help_text='Select file to attach', null=True, upload_to=part.models.attach_file), - ), - migrations.AlterField( + migrations.AddField( model_name='supplierpart', name='part', field=models.ForeignKey(help_text='Select part', limit_choices_to={'purchaseable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='supplier_parts', to='part.Part'), ), - migrations.AlterField( + migrations.AddField( model_name='supplierpart', name='supplier', field=models.ForeignKey(help_text='Select supplier', limit_choices_to={'is_supplier': True}, on_delete=django.db.models.deletion.CASCADE, related_name='parts', to='company.Company'), ), + migrations.AddField( + model_name='contact', + name='company', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contacts', to='company.Company'), + ), + migrations.AlterUniqueTogether( + name='supplierpricebreak', + unique_together={('part', 'quantity')}, + ), + migrations.AlterUniqueTogether( + name='supplierpart', + unique_together={('part', 'supplier', 'SKU')}, + ), ] diff --git a/InvenTree/company/migrations/0003_auto_20180423_1117.py b/InvenTree/company/migrations/0003_auto_20180423_1117.py deleted file mode 100644 index 638e3a1ab7..0000000000 --- a/InvenTree/company/migrations/0003_auto_20180423_1117.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.12 on 2018-04-23 11:17 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0002_auto_20180422_1201'), - ] - - operations = [ - migrations.AlterField( - model_name='company', - name='is_supplier', - field=models.BooleanField(default=True), - ), - migrations.AlterField( - model_name='company', - name='name', - field=models.CharField(help_text='Company name', max_length=100, unique=True), - ), - ] diff --git a/InvenTree/company/migrations/0004_company_url.py b/InvenTree/company/migrations/0004_company_url.py deleted file mode 100644 index e478757394..0000000000 --- a/InvenTree/company/migrations/0004_company_url.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.12 on 2018-04-24 08:01 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0003_auto_20180423_1117'), - ] - - operations = [ - migrations.AddField( - model_name='company', - name='URL', - field=models.URLField(blank=True, help_text='Link to external company information'), - ), - ] diff --git a/InvenTree/company/migrations/0005_contact.py b/InvenTree/company/migrations/0005_contact.py deleted file mode 100644 index 8a668a0590..0000000000 --- a/InvenTree/company/migrations/0005_contact.py +++ /dev/null @@ -1,27 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.12 on 2018-04-30 07:19 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0004_company_url'), - ] - - operations = [ - migrations.CreateModel( - name='Contact', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=100)), - ('phone', models.CharField(blank=True, max_length=100)), - ('email', models.EmailField(blank=True, max_length=254)), - ('role', models.CharField(blank=True, max_length=100)), - ('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contacts', to='company.Company')), - ], - ), - ] diff --git a/InvenTree/company/migrations/0006_auto_20190508_2332.py b/InvenTree/company/migrations/0006_auto_20190508_2332.py deleted file mode 100644 index 232ccdd4f1..0000000000 --- a/InvenTree/company/migrations/0006_auto_20190508_2332.py +++ /dev/null @@ -1,43 +0,0 @@ -# Generated by Django 2.2 on 2019-05-08 13:32 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0005_contact'), - ] - - operations = [ - migrations.AlterField( - model_name='company', - name='contact', - field=models.CharField(blank=True, help_text='Point of contact', max_length=100), - ), - migrations.AlterField( - model_name='company', - name='description', - field=models.CharField(help_text='Description of the company', max_length=500), - ), - migrations.AlterField( - model_name='company', - name='email', - field=models.EmailField(blank=True, help_text='Contact email address', max_length=254), - ), - migrations.AlterField( - model_name='company', - name='is_customer', - field=models.BooleanField(default=False, help_text='Do you sell items to this company?'), - ), - migrations.AlterField( - model_name='company', - name='is_supplier', - field=models.BooleanField(default=True, help_text='Do you purchase items from this company?'), - ), - migrations.AlterField( - model_name='company', - name='phone', - field=models.CharField(blank=True, help_text='Contact phone number', max_length=50), - ), - ] diff --git a/InvenTree/company/migrations/0007_supplierpart_supplierpricebreak.py b/InvenTree/company/migrations/0007_supplierpart_supplierpricebreak.py deleted file mode 100644 index f3cc3ea6de..0000000000 --- a/InvenTree/company/migrations/0007_supplierpart_supplierpricebreak.py +++ /dev/null @@ -1,52 +0,0 @@ -# Generated by Django 2.2 on 2019-05-18 07:59 - -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0032_auto_20190518_1759'), - ('company', '0006_auto_20190508_2332'), - ] - - operations = [ - migrations.CreateModel( - name='SupplierPart', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('SKU', models.CharField(help_text='Supplier stock keeping unit', max_length=100)), - ('manufacturer', models.CharField(blank=True, help_text='Manufacturer', max_length=100)), - ('MPN', models.CharField(blank=True, help_text='Manufacturer part number', max_length=100)), - ('URL', models.URLField(blank=True, help_text='URL for external supplier part link')), - ('description', models.CharField(blank=True, help_text='Supplier part description', max_length=250)), - ('note', models.CharField(blank=True, help_text='Notes', max_length=100)), - ('base_cost', models.DecimalField(decimal_places=3, default=0, help_text='Minimum charge (e.g. stocking fee)', max_digits=10, validators=[django.core.validators.MinValueValidator(0)])), - ('packaging', models.CharField(blank=True, help_text='Part packaging', max_length=50)), - ('multiple', models.PositiveIntegerField(default=1, help_text='Order multiple', validators=[django.core.validators.MinValueValidator(1)])), - ('minimum', models.PositiveIntegerField(default=1, help_text='Minimum order quantity (MOQ)', validators=[django.core.validators.MinValueValidator(1)])), - ('lead_time', models.DurationField(blank=True, null=True)), - ('part', models.ForeignKey(help_text='Select part', limit_choices_to={'purchaseable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='supplier_parts', to='part.Part')), - ('supplier', models.ForeignKey(help_text='Select supplier', limit_choices_to={'is_supplier': True}, on_delete=django.db.models.deletion.CASCADE, related_name='parts', to='company.Company')), - ], - options={ - 'db_table': 'part_supplierpart', - 'unique_together': {('part', 'supplier', 'SKU')}, - }, - ), - migrations.CreateModel( - name='SupplierPriceBreak', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('quantity', models.PositiveIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1)])), - ('cost', models.DecimalField(decimal_places=3, max_digits=10, validators=[django.core.validators.MinValueValidator(0)])), - ('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pricebreaks', to='company.SupplierPart')), - ], - options={ - 'db_table': 'part_supplierpricebreak', - 'unique_together': {('part', 'quantity')}, - }, - ), - ] diff --git a/InvenTree/company/migrations/0008_auto_20190518_2120.py b/InvenTree/company/migrations/0008_auto_20190518_2120.py deleted file mode 100644 index ef9c318f52..0000000000 --- a/InvenTree/company/migrations/0008_auto_20190518_2120.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-05-18 11:20 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0007_supplierpart_supplierpricebreak'), - ] - - operations = [ - migrations.AlterField( - model_name='supplierpricebreak', - name='cost', - field=models.DecimalField(decimal_places=5, max_digits=10, validators=[django.core.validators.MinValueValidator(0)]), - ), - ] diff --git a/InvenTree/part/migrations/0001_initial.py b/InvenTree/part/migrations/0001_initial.py index 361436047e..c0413a909a 100644 --- a/InvenTree/part/migrations/0001_initial.py +++ b/InvenTree/part/migrations/0001_initial.py @@ -1,7 +1,7 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.12 on 2018-04-22 11:53 -from __future__ import unicode_literals +# Generated by Django 2.2 on 2019-05-18 14:04 +import InvenTree.validators +from django.conf import settings import django.core.validators from django.db import migrations, models import django.db.models.deletion @@ -13,7 +13,7 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('company', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ @@ -21,7 +21,9 @@ class Migration(migrations.Migration): name='BomItem', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('quantity', models.PositiveIntegerField(default=1, validators=[django.core.validators.MinValueValidator(0)])), + ('quantity', models.PositiveIntegerField(default=1, help_text='BOM quantity for this BOM item', validators=[django.core.validators.MinValueValidator(0)])), + ('overage', models.CharField(blank=True, help_text='Estimated build wastage quantity (absolute or percentage)', max_length=24, validators=[InvenTree.validators.validate_overage])), + ('note', models.CharField(blank=True, help_text='BOM item notes', max_length=100)), ], options={ 'verbose_name': 'BOM Item', @@ -31,18 +33,24 @@ class Migration(migrations.Migration): name='Part', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(help_text='Part name (must be unique)', max_length=100, unique=True)), + ('name', models.CharField(help_text='Part name', max_length=100, validators=[InvenTree.validators.validate_part_name])), + ('variant', models.CharField(blank=True, help_text='Part variant or revision code', max_length=32)), ('description', models.CharField(help_text='Part description', max_length=250)), + ('keywords', models.CharField(blank=True, help_text='Part keywords to improve visibility in search results', max_length=250)), ('IPN', models.CharField(blank=True, help_text='Internal Part Number', max_length=100)), ('URL', models.URLField(blank=True, help_text='Link to extenal URL')), ('image', models.ImageField(blank=True, max_length=255, null=True, upload_to=part.models.rename_part_image)), ('minimum_stock', models.PositiveIntegerField(default=0, help_text='Minimum allowed stock level', validators=[django.core.validators.MinValueValidator(0)])), - ('units', models.CharField(blank=True, default='pcs', max_length=20)), + ('units', models.CharField(blank=True, default='pcs', help_text='Stock keeping units for this part', max_length=20)), ('buildable', models.BooleanField(default=False, help_text='Can this part be built from other parts?')), + ('consumable', models.BooleanField(default=True, help_text='Can this part be used to build other parts?')), ('trackable', models.BooleanField(default=False, help_text='Does this part have tracking for unique items?')), ('purchaseable', models.BooleanField(default=True, help_text='Can this part be purchased from external suppliers?')), ('salable', models.BooleanField(default=False, help_text='Can this part be sold to customers?')), + ('active', models.BooleanField(default=True, help_text='Is this part active?')), ('notes', models.TextField(blank=True)), + ('bom_checksum', models.CharField(blank=True, help_text='Stored BOM checksum', max_length=128)), + ('bom_checked_date', models.DateField(blank=True, null=True)), ], options={ 'verbose_name': 'Part', @@ -53,8 +61,8 @@ class Migration(migrations.Migration): name='PartAttachment', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('attachment', models.FileField(blank=True, null=True, upload_to=part.models.attach_file)), - ('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='part.Part')), + ('attachment', models.FileField(help_text='Select file to attach', upload_to=part.models.attach_file)), + ('comment', models.CharField(help_text='File comment', max_length=100)), ], ), migrations.CreateModel( @@ -63,7 +71,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=100, unique=True)), ('description', models.CharField(max_length=250)), - ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='children', to='part.PartCategory')), + ('default_keywords', models.CharField(blank=True, help_text='Default keywords for parts in this category', max_length=250)), ], options={ 'verbose_name': 'Part Category', @@ -71,63 +79,11 @@ class Migration(migrations.Migration): }, ), migrations.CreateModel( - name='SupplierPart', + name='PartStar', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('SKU', models.CharField(help_text='Supplier stock keeping unit', max_length=100)), - ('manufacturer', models.CharField(blank=True, help_text='Manufacturer', max_length=100)), - ('MPN', models.CharField(blank=True, help_text='Manufacturer part number', max_length=100)), - ('URL', models.URLField(blank=True)), - ('description', models.CharField(blank=True, max_length=250)), - ('single_price', models.DecimalField(decimal_places=3, default=0, max_digits=10)), - ('base_cost', models.DecimalField(decimal_places=3, default=0, max_digits=10)), - ('packaging', models.CharField(blank=True, max_length=50)), - ('multiple', models.PositiveIntegerField(default=1, validators=[django.core.validators.MinValueValidator(0)])), - ('minimum', models.PositiveIntegerField(default=1, validators=[django.core.validators.MinValueValidator(0)])), - ('lead_time', models.DurationField(blank=True, null=True)), - ('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='supplier_parts', to='part.Part')), - ('supplier', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='parts', to='company.Company')), + ('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='starred_users', to='part.Part')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='starred_parts', to=settings.AUTH_USER_MODEL)), ], ), - migrations.CreateModel( - name='SupplierPriceBreak', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('quantity', models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(0)])), - ('cost', models.DecimalField(decimal_places=3, max_digits=10)), - ('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='price_breaks', to='part.SupplierPart')), - ], - ), - migrations.AddField( - model_name='part', - name='category', - field=models.ForeignKey(blank=True, help_text='Part category', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='parts', to='part.PartCategory'), - ), - migrations.AddField( - model_name='part', - name='default_supplier', - field=models.ForeignKey(blank=True, help_text='Default supplier part', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_parts', to='part.SupplierPart'), - ), - migrations.AddField( - model_name='bomitem', - name='part', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bom_items', to='part.Part'), - ), - migrations.AddField( - model_name='bomitem', - name='sub_part', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='used_in', to='part.Part'), - ), - migrations.AlterUniqueTogether( - name='supplierpricebreak', - unique_together=set([('part', 'quantity')]), - ), - migrations.AlterUniqueTogether( - name='supplierpart', - unique_together=set([('part', 'supplier', 'SKU')]), - ), - migrations.AlterUniqueTogether( - name='bomitem', - unique_together=set([('part', 'sub_part')]), - ), ] diff --git a/InvenTree/part/migrations/0002_auto_20190519_0004.py b/InvenTree/part/migrations/0002_auto_20190519_0004.py new file mode 100644 index 0000000000..02df1a7fad --- /dev/null +++ b/InvenTree/part/migrations/0002_auto_20190519_0004.py @@ -0,0 +1,77 @@ +# Generated by Django 2.2 on 2019-05-18 14:04 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('company', '0002_auto_20190519_0004'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('stock', '0001_initial'), + ('part', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='partcategory', + name='default_location', + field=models.ForeignKey(blank=True, help_text='Default location for parts in this category', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_categories', to='stock.StockLocation'), + ), + migrations.AddField( + model_name='partcategory', + name='parent', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='children', to='part.PartCategory'), + ), + migrations.AddField( + model_name='partattachment', + name='part', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='part.Part'), + ), + migrations.AddField( + model_name='part', + name='bom_checked_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='boms_checked', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='part', + name='category', + field=models.ForeignKey(blank=True, help_text='Part category', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='parts', to='part.PartCategory'), + ), + migrations.AddField( + model_name='part', + name='default_location', + field=models.ForeignKey(blank=True, help_text='Where is this item normally stored?', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_parts', to='stock.StockLocation'), + ), + migrations.AddField( + model_name='part', + name='default_supplier', + field=models.ForeignKey(blank=True, help_text='Default supplier part', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_parts', to='company.SupplierPart'), + ), + migrations.AddField( + model_name='bomitem', + name='part', + field=models.ForeignKey(help_text='Select parent part', limit_choices_to={'active': True, 'buildable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='bom_items', to='part.Part'), + ), + migrations.AddField( + model_name='bomitem', + name='sub_part', + field=models.ForeignKey(help_text='Select part to be used in BOM', limit_choices_to={'active': True, 'consumable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='used_in', to='part.Part'), + ), + migrations.AlterUniqueTogether( + name='partstar', + unique_together={('part', 'user')}, + ), + migrations.AlterUniqueTogether( + name='part', + unique_together={('name', 'variant')}, + ), + migrations.AlterUniqueTogether( + name='bomitem', + unique_together={('part', 'sub_part')}, + ), + ] diff --git a/InvenTree/part/migrations/0002_part_default_location.py b/InvenTree/part/migrations/0002_part_default_location.py deleted file mode 100644 index c5a65d284d..0000000000 --- a/InvenTree/part/migrations/0002_part_default_location.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.12 on 2018-04-22 11:53 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0001_initial'), - ('part', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='part', - name='default_location', - field=models.ForeignKey(blank=True, help_text='Where is this item normally stored?', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_parts', to='stock.StockLocation'), - ), - ] diff --git a/InvenTree/part/migrations/0003_auto_20190412_2030.py b/InvenTree/part/migrations/0003_auto_20190412_2030.py deleted file mode 100644 index 3439c09483..0000000000 --- a/InvenTree/part/migrations/0003_auto_20190412_2030.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-04-12 10:30 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0002_part_default_location'), - ] - - operations = [ - migrations.AlterField( - model_name='bomitem', - name='part', - field=models.ForeignKey(limit_choices_to={'buildable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='bom_items', to='part.Part'), - ), - ] diff --git a/InvenTree/part/migrations/0004_bomitem_note.py b/InvenTree/part/migrations/0004_bomitem_note.py deleted file mode 100644 index 69bea83175..0000000000 --- a/InvenTree/part/migrations/0004_bomitem_note.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2 on 2019-04-14 08:25 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0003_auto_20190412_2030'), - ] - - operations = [ - migrations.AddField( - model_name='bomitem', - name='note', - field=models.CharField(blank=True, help_text='Item notes', max_length=100), - ), - ] diff --git a/InvenTree/part/migrations/0005_part_consumable.py b/InvenTree/part/migrations/0005_part_consumable.py deleted file mode 100644 index 4bb1056ae7..0000000000 --- a/InvenTree/part/migrations/0005_part_consumable.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2 on 2019-04-15 13:48 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0004_bomitem_note'), - ] - - operations = [ - migrations.AddField( - model_name='part', - name='consumable', - field=models.BooleanField(default=False, help_text='Can this part be used to build other parts?'), - ), - ] diff --git a/InvenTree/part/migrations/0006_auto_20190416_2354.py b/InvenTree/part/migrations/0006_auto_20190416_2354.py deleted file mode 100644 index f2b2f42398..0000000000 --- a/InvenTree/part/migrations/0006_auto_20190416_2354.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 2.2 on 2019-04-16 13:54 - -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0005_part_consumable'), - ] - - operations = [ - migrations.AlterField( - model_name='bomitem', - name='sub_part', - field=models.ForeignKey(limit_choices_to={'consumable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='used_in', to='part.Part'), - ), - migrations.AlterField( - model_name='part', - name='consumable', - field=models.BooleanField(default=True, help_text='Can this part be used to build other parts?'), - ), - migrations.AlterField( - model_name='supplierpricebreak', - name='quantity', - field=models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(2)]), - ), - ] diff --git a/InvenTree/part/migrations/0007_auto_20190417_0007.py b/InvenTree/part/migrations/0007_auto_20190417_0007.py deleted file mode 100644 index 554a471132..0000000000 --- a/InvenTree/part/migrations/0007_auto_20190417_0007.py +++ /dev/null @@ -1,54 +0,0 @@ -# Generated by Django 2.2 on 2019-04-16 14:07 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0006_auto_20190416_2354'), - ] - - operations = [ - migrations.AddField( - model_name='supplierpart', - name='note', - field=models.CharField(blank=True, help_text='Notes', max_length=100), - ), - migrations.AlterField( - model_name='supplierpart', - name='base_cost', - field=models.DecimalField(decimal_places=3, default=0, help_text='Minimum charge (e.g. stocking fee)', max_digits=10, validators=[django.core.validators.MinValueValidator(0)]), - ), - migrations.AlterField( - model_name='supplierpart', - name='description', - field=models.CharField(blank=True, help_text='Supplier part description', max_length=250), - ), - migrations.AlterField( - model_name='supplierpart', - name='minimum', - field=models.PositiveIntegerField(default=1, help_text='Minimum order quantity (MOQ)', validators=[django.core.validators.MinValueValidator(0)]), - ), - migrations.AlterField( - model_name='supplierpart', - name='multiple', - field=models.PositiveIntegerField(default=1, help_text='Order multiple', validators=[django.core.validators.MinValueValidator(0)]), - ), - migrations.AlterField( - model_name='supplierpart', - name='packaging', - field=models.CharField(blank=True, help_text='Part packaging', max_length=50), - ), - migrations.AlterField( - model_name='supplierpart', - name='single_price', - field=models.DecimalField(decimal_places=3, default=0, help_text='Price for single quantity', max_digits=10, validators=[django.core.validators.MinValueValidator(0)]), - ), - migrations.AlterField( - model_name='supplierpricebreak', - name='cost', - field=models.DecimalField(decimal_places=3, max_digits=10, validators=[django.core.validators.MinValueValidator(0)]), - ), - ] diff --git a/InvenTree/part/migrations/0008_auto_20190417_0013.py b/InvenTree/part/migrations/0008_auto_20190417_0013.py deleted file mode 100644 index e7311e2be8..0000000000 --- a/InvenTree/part/migrations/0008_auto_20190417_0013.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-04-16 14:13 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0007_auto_20190417_0007'), - ] - - operations = [ - migrations.AlterField( - model_name='supplierpart', - name='part', - field=models.ForeignKey(limit_choices_to={'purchasable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='supplier_parts', to='part.Part'), - ), - ] diff --git a/InvenTree/part/migrations/0009_auto_20190417_0019.py b/InvenTree/part/migrations/0009_auto_20190417_0019.py deleted file mode 100644 index 2a6eba4960..0000000000 --- a/InvenTree/part/migrations/0009_auto_20190417_0019.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-04-16 14:19 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0008_auto_20190417_0013'), - ] - - operations = [ - migrations.AlterField( - model_name='supplierpart', - name='part', - field=models.ForeignKey(limit_choices_to={'purchaseable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='supplier_parts', to='part.Part'), - ), - ] diff --git a/InvenTree/part/migrations/0010_auto_20190417_0045.py b/InvenTree/part/migrations/0010_auto_20190417_0045.py deleted file mode 100644 index 1040afc67c..0000000000 --- a/InvenTree/part/migrations/0010_auto_20190417_0045.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 2.2 on 2019-04-16 14:45 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0009_auto_20190417_0019'), - ] - - operations = [ - migrations.AlterField( - model_name='supplierpart', - name='minimum', - field=models.PositiveIntegerField(default=1, help_text='Minimum order quantity (MOQ)', validators=[django.core.validators.MinValueValidator(1)]), - ), - migrations.AlterField( - model_name='supplierpart', - name='multiple', - field=models.PositiveIntegerField(default=1, help_text='Order multiple', validators=[django.core.validators.MinValueValidator(1)]), - ), - ] diff --git a/InvenTree/part/migrations/0011_auto_20190428_0841.py b/InvenTree/part/migrations/0011_auto_20190428_0841.py deleted file mode 100644 index 0146b6795a..0000000000 --- a/InvenTree/part/migrations/0011_auto_20190428_0841.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-04-27 22:41 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0010_auto_20190417_0045'), - ] - - operations = [ - migrations.AlterField( - model_name='supplierpart', - name='supplier', - field=models.ForeignKey(limit_choices_to={'is_supplier': True}, on_delete=django.db.models.deletion.CASCADE, related_name='parts', to='company.Company'), - ), - ] diff --git a/InvenTree/part/migrations/0012_part_active.py b/InvenTree/part/migrations/0012_part_active.py deleted file mode 100644 index 87929e2f85..0000000000 --- a/InvenTree/part/migrations/0012_part_active.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 2.2 on 2019-04-28 13:00 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0011_auto_20190428_0841'), - ] - - operations = [ - migrations.AddField( - model_name='part', - name='active', - field=models.BooleanField(default=True, help_text='Is this part active?'), - ), - migrations.AddField( - model_name='partattachment', - name='comment', - field=models.CharField(blank=True, help_text='File comment', max_length=100), - ), - ] diff --git a/InvenTree/part/migrations/0013_auto_20190429_2229.py b/InvenTree/part/migrations/0013_auto_20190429_2229.py deleted file mode 100644 index 9c339b18b1..0000000000 --- a/InvenTree/part/migrations/0013_auto_20190429_2229.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2 on 2019-04-29 12:29 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0012_part_active'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='URL', - field=models.URLField(blank=True, help_text='Link to external URL'), - ), - ] diff --git a/InvenTree/part/migrations/0014_auto_20190502_2039.py b/InvenTree/part/migrations/0014_auto_20190502_2039.py deleted file mode 100644 index c32c3afe32..0000000000 --- a/InvenTree/part/migrations/0014_auto_20190502_2039.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2 on 2019-05-02 10:39 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0013_auto_20190429_2229'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='URL', - field=models.URLField(blank=True, help_text='Link to extenal URL'), - ), - ] diff --git a/InvenTree/part/migrations/0015_partcategory_default_location.py b/InvenTree/part/migrations/0015_partcategory_default_location.py deleted file mode 100644 index 33a47cc440..0000000000 --- a/InvenTree/part/migrations/0015_partcategory_default_location.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 2.2 on 2019-05-04 08:57 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0013_remove_stockitem_uuid'), - ('part', '0014_auto_20190502_2039'), - ] - - operations = [ - migrations.AddField( - model_name='partcategory', - name='default_location', - field=models.ForeignKey(blank=True, help_text='Default location for parts in this category', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_categories', to='stock.StockLocation'), - ), - ] diff --git a/InvenTree/part/migrations/0016_partstar.py b/InvenTree/part/migrations/0016_partstar.py deleted file mode 100644 index baa5c83d5b..0000000000 --- a/InvenTree/part/migrations/0016_partstar.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 2.2 on 2019-05-04 22:45 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('part', '0015_partcategory_default_location'), - ] - - operations = [ - migrations.CreateModel( - name='PartStar', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='starred_users', to='part.Part')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='starred_parts', to=settings.AUTH_USER_MODEL)), - ], - ), - ] diff --git a/InvenTree/part/migrations/0017_auto_20190505_0848.py b/InvenTree/part/migrations/0017_auto_20190505_0848.py deleted file mode 100644 index 90162132f9..0000000000 --- a/InvenTree/part/migrations/0017_auto_20190505_0848.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-05-04 22:48 - -from django.conf import settings -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('part', '0016_partstar'), - ] - - operations = [ - migrations.AlterUniqueTogether( - name='partstar', - unique_together={('part', 'user')}, - ), - ] diff --git a/InvenTree/part/migrations/0018_auto_20190505_2231.py b/InvenTree/part/migrations/0018_auto_20190505_2231.py deleted file mode 100644 index 32aa1e3a1b..0000000000 --- a/InvenTree/part/migrations/0018_auto_20190505_2231.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 2.2 on 2019-05-05 12:31 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0017_auto_20190505_0848'), - ] - - operations = [ - migrations.AlterField( - model_name='bomitem', - name='part', - field=models.ForeignKey(limit_choices_to={'active': True, 'buildable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='bom_items', to='part.Part'), - ), - migrations.AlterField( - model_name='bomitem', - name='sub_part', - field=models.ForeignKey(limit_choices_to={'active': True, 'consumable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='used_in', to='part.Part'), - ), - ] diff --git a/InvenTree/part/migrations/0020_auto_20190510_2022.py b/InvenTree/part/migrations/0020_auto_20190510_2022.py deleted file mode 100644 index 8bf6db8f18..0000000000 --- a/InvenTree/part/migrations/0020_auto_20190510_2022.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 2.2 on 2019-05-10 10:22 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0019_auto_20190508_2332'), - ] - - operations = [ - migrations.AddField( - model_name='part', - name='variant', - field=models.CharField(blank=True, help_text='Part variant or revision code', max_length=32), - ), - migrations.AlterField( - model_name='part', - name='name', - field=models.CharField(help_text='Part name', max_length=100), - ), - migrations.AlterUniqueTogether( - name='part', - unique_together={('name', 'variant')}, - ), - ] diff --git a/InvenTree/part/migrations/0021_auto_20190510_2220.py b/InvenTree/part/migrations/0021_auto_20190510_2220.py deleted file mode 100644 index 294bd112ae..0000000000 --- a/InvenTree/part/migrations/0021_auto_20190510_2220.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-05-10 12:20 - -import InvenTree.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0020_auto_20190510_2022'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='name', - field=models.CharField(help_text='Part name', max_length=100, validators=[InvenTree.validators.validate_part_name]), - ), - ] diff --git a/InvenTree/part/migrations/0022_auto_20190512_1246.py b/InvenTree/part/migrations/0022_auto_20190512_1246.py deleted file mode 100644 index 40f4d0dd4c..0000000000 --- a/InvenTree/part/migrations/0022_auto_20190512_1246.py +++ /dev/null @@ -1,31 +0,0 @@ -# Generated by Django 2.2 on 2019-05-12 02:46 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('part', '0021_auto_20190510_2220'), - ] - - operations = [ - migrations.AddField( - model_name='part', - name='bom_checked_by', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='boms_checked', to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='part', - name='bom_checked_date', - field=models.DateField(blank=True, null=True), - ), - migrations.AddField( - model_name='part', - name='bom_checksum', - field=models.CharField(blank=True, help_text='Stored BOM checksum', max_length=128), - ), - ] diff --git a/InvenTree/part/migrations/0023_part_keywords.py b/InvenTree/part/migrations/0023_part_keywords.py deleted file mode 100644 index 4752d80740..0000000000 --- a/InvenTree/part/migrations/0023_part_keywords.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2 on 2019-05-14 07:15 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0022_auto_20190512_1246'), - ] - - operations = [ - migrations.AddField( - model_name='part', - name='keywords', - field=models.CharField(blank=True, help_text='Part keywords to improve visibility in search results', max_length=250), - ), - ] diff --git a/InvenTree/part/migrations/0024_partcategory_default_keywords.py b/InvenTree/part/migrations/0024_partcategory_default_keywords.py deleted file mode 100644 index 317d982f7d..0000000000 --- a/InvenTree/part/migrations/0024_partcategory_default_keywords.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2 on 2019-05-14 07:27 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0023_part_keywords'), - ] - - operations = [ - migrations.AddField( - model_name='partcategory', - name='default_keywords', - field=models.CharField(blank=True, help_text='Default keywords for parts in this category', max_length=250), - ), - ] diff --git a/InvenTree/part/migrations/0025_auto_20190515_0012.py b/InvenTree/part/migrations/0025_auto_20190515_0012.py deleted file mode 100644 index aaeb8ea1a3..0000000000 --- a/InvenTree/part/migrations/0025_auto_20190515_0012.py +++ /dev/null @@ -1,46 +0,0 @@ -# Generated by Django 2.2 on 2019-05-14 14:12 - -import InvenTree.validators -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0024_partcategory_default_keywords'), - ] - - operations = [ - migrations.AddField( - model_name='bomitem', - name='overage', - field=models.CharField(blank=True, help_text='Estimated build wastage quantity (absolute or percentage)', max_length=24, validators=[InvenTree.validators.validate_overage]), - ), - migrations.AlterField( - model_name='bomitem', - name='note', - field=models.CharField(blank=True, help_text='BOM item notes', max_length=100), - ), - migrations.AlterField( - model_name='bomitem', - name='part', - field=models.ForeignKey(help_text='Select parent part', limit_choices_to={'active': True, 'buildable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='bom_items', to='part.Part'), - ), - migrations.AlterField( - model_name='bomitem', - name='quantity', - field=models.PositiveIntegerField(default=1, help_text='BOM quantity for this BOM item', validators=[django.core.validators.MinValueValidator(0)]), - ), - migrations.AlterField( - model_name='bomitem', - name='sub_part', - field=models.ForeignKey(help_text='Select part to be used in BOM', limit_choices_to={'active': True, 'consumable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='used_in', to='part.Part'), - ), - migrations.AlterField( - model_name='supplierpart', - name='URL', - field=models.URLField(blank=True, help_text='URL for external supplier part link'), - ), - ] diff --git a/InvenTree/part/migrations/0026_remove_supplierpart_single_price.py b/InvenTree/part/migrations/0026_remove_supplierpart_single_price.py deleted file mode 100644 index f0bde887a4..0000000000 --- a/InvenTree/part/migrations/0026_remove_supplierpart_single_price.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 2.2 on 2019-05-18 05:43 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0025_auto_20190515_0012'), - ] - - operations = [ - migrations.RemoveField( - model_name='supplierpart', - name='single_price', - ), - ] diff --git a/InvenTree/part/migrations/0027_auto_20190518_1620.py b/InvenTree/part/migrations/0027_auto_20190518_1620.py deleted file mode 100644 index 85ed3e6be2..0000000000 --- a/InvenTree/part/migrations/0027_auto_20190518_1620.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2 on 2019-05-18 06:20 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0026_remove_supplierpart_single_price'), - ] - - operations = [ - migrations.AlterField( - model_name='partattachment', - name='comment', - field=models.CharField(help_text='File comment', max_length=100), - ), - ] diff --git a/InvenTree/part/migrations/0028_auto_20190518_1627.py b/InvenTree/part/migrations/0028_auto_20190518_1627.py deleted file mode 100644 index fa0fa1457c..0000000000 --- a/InvenTree/part/migrations/0028_auto_20190518_1627.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-05-18 06:27 - -from django.db import migrations, models -import part.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0027_auto_20190518_1620'), - ] - - operations = [ - migrations.AlterField( - model_name='partattachment', - name='attachment', - field=models.FileField(help_text='Select file to attach', upload_to=part.models.attach_file), - ), - ] diff --git a/InvenTree/part/migrations/0029_auto_20190518_1632.py b/InvenTree/part/migrations/0029_auto_20190518_1632.py deleted file mode 100644 index a5a2d61809..0000000000 --- a/InvenTree/part/migrations/0029_auto_20190518_1632.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-05-18 06:32 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0028_auto_20190518_1627'), - ] - - operations = [ - migrations.AlterField( - model_name='supplierpricebreak', - name='quantity', - field=models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(1)]), - ), - ] diff --git a/InvenTree/part/migrations/0030_auto_20190518_1641.py b/InvenTree/part/migrations/0030_auto_20190518_1641.py deleted file mode 100644 index 7661dd6412..0000000000 --- a/InvenTree/part/migrations/0030_auto_20190518_1641.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-05-18 06:41 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0029_auto_20190518_1632'), - ] - - operations = [ - migrations.AlterField( - model_name='supplierpricebreak', - name='part', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pricebreaks', to='part.SupplierPart'), - ), - ] diff --git a/InvenTree/part/migrations/0031_auto_20190518_1650.py b/InvenTree/part/migrations/0031_auto_20190518_1650.py deleted file mode 100644 index 67c6d6124f..0000000000 --- a/InvenTree/part/migrations/0031_auto_20190518_1650.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-05-18 06:50 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0030_auto_20190518_1641'), - ] - - operations = [ - migrations.AlterField( - model_name='supplierpricebreak', - name='quantity', - field=models.PositiveIntegerField(default=1, validators=[django.core.validators.MinValueValidator(1)]), - ), - ] diff --git a/InvenTree/part/migrations/0032_auto_20190518_1759.py b/InvenTree/part/migrations/0032_auto_20190518_1759.py deleted file mode 100644 index 8a82961947..0000000000 --- a/InvenTree/part/migrations/0032_auto_20190518_1759.py +++ /dev/null @@ -1,34 +0,0 @@ -# Generated by Django 2.2 on 2019-05-18 07:59 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0017_auto_20190518_1759'), - ('part', '0031_auto_20190518_1650'), - ] - - operations = [ - migrations.AlterUniqueTogether( - name='supplierpricebreak', - unique_together=None, - ), - migrations.RemoveField( - model_name='supplierpricebreak', - name='part', - ), - migrations.AlterField( - model_name='part', - name='default_supplier', - field=models.ForeignKey(blank=True, help_text='Default supplier part', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_parts', to='company.SupplierPart'), - ), - migrations.DeleteModel( - name='SupplierPart', - ), - migrations.DeleteModel( - name='SupplierPriceBreak', - ), - ] diff --git a/InvenTree/stock/migrations/0001_initial.py b/InvenTree/stock/migrations/0001_initial.py index b4d15a7f64..2fc4e96608 100644 --- a/InvenTree/stock/migrations/0001_initial.py +++ b/InvenTree/stock/migrations/0001_initial.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.12 on 2018-04-22 11:53 -from __future__ import unicode_literals +# Generated by Django 2.2 on 2019-05-18 14:04 from django.conf import settings import django.core.validators @@ -14,8 +12,8 @@ class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('part', '0001_initial'), ('company', '0001_initial'), + ('part', '0001_initial'), ] operations = [ @@ -25,30 +23,19 @@ class Migration(migrations.Migration): ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('serial', models.PositiveIntegerField(blank=True, help_text='Serial number for this item', null=True)), ('URL', models.URLField(blank=True, max_length=125)), - ('batch', models.CharField(blank=True, help_text='Batch code for this stock item', max_length=100)), - ('quantity', models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(0)])), - ('updated', models.DateField(auto_now=True)), + ('batch', models.CharField(blank=True, help_text='Batch code for this stock item', max_length=100, null=True)), + ('quantity', models.PositiveIntegerField(default=1, validators=[django.core.validators.MinValueValidator(0)])), + ('updated', models.DateField(auto_now=True, null=True)), ('stocktake_date', models.DateField(blank=True, null=True)), ('review_needed', models.BooleanField(default=False)), - ('status', models.PositiveIntegerField(choices=[(10, 'OK'), (60, 'Destroyed'), (50, 'Attention needed'), (55, 'Damaged')], default=10, validators=[django.core.validators.MinValueValidator(0)])), - ('notes', models.TextField(blank=True)), + ('delete_on_deplete', models.BooleanField(default=True, help_text='Delete this Stock Item when stock is depleted')), + ('status', models.PositiveIntegerField(choices=[(10, 'OK'), (50, 'Attention needed'), (55, 'Damaged'), (60, 'Destroyed')], default=10, validators=[django.core.validators.MinValueValidator(0)])), + ('notes', models.CharField(blank=True, help_text='Stock Item Notes', max_length=250)), ('infinite', models.BooleanField(default=False)), ('belongs_to', models.ForeignKey(blank=True, help_text='Is this item installed in another item?', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='owned_parts', to='stock.StockItem')), ('customer', models.ForeignKey(blank=True, help_text='Item assigned to customer?', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stockitems', to='company.Company')), ], ), - migrations.CreateModel( - name='StockItemTracking', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('date', models.DateField(auto_now_add=True)), - ('title', models.CharField(max_length=250)), - ('notes', models.TextField(blank=True)), - ('system', models.BooleanField(default=False)), - ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tracking_info', to='stock.StockItem')), - ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), - ], - ), migrations.CreateModel( name='StockLocation', fields=[ @@ -59,34 +46,44 @@ class Migration(migrations.Migration): ], options={ 'abstract': False, + 'unique_together': {('name', 'parent')}, }, ), + migrations.CreateModel( + name='StockItemTracking', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date', models.DateTimeField(auto_now_add=True)), + ('title', models.CharField(max_length=250)), + ('notes', models.TextField(blank=True)), + ('system', models.BooleanField(default=False)), + ('quantity', models.PositiveIntegerField(default=1, validators=[django.core.validators.MinValueValidator(0)])), + ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tracking_info', to='stock.StockItem')), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ], + ), migrations.AddField( model_name='stockitem', name='location', - field=models.ForeignKey(blank=True, help_text='Where is this stock item located?', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='items', to='stock.StockLocation'), + field=models.ForeignKey(blank=True, help_text='Where is this stock item located?', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='stock_items', to='stock.StockLocation'), ), migrations.AddField( model_name='stockitem', name='part', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='locations', to='part.Part'), + field=models.ForeignKey(help_text='Base part', on_delete=django.db.models.deletion.CASCADE, related_name='locations', to='part.Part'), ), migrations.AddField( model_name='stockitem', name='stocktake_user', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stocktake_stock', to=settings.AUTH_USER_MODEL), ), migrations.AddField( model_name='stockitem', name='supplier_part', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='part.SupplierPart'), - ), - migrations.AlterUniqueTogether( - name='stocklocation', - unique_together=set([('name', 'parent')]), + field=models.ForeignKey(blank=True, help_text='Select a matching supplier part for this stock item', null=True, on_delete=django.db.models.deletion.SET_NULL, to='company.SupplierPart'), ), migrations.AlterUniqueTogether( name='stockitem', - unique_together=set([('part', 'serial')]), + unique_together={('part', 'serial')}, ), ] diff --git a/InvenTree/stock/migrations/0002_auto_20180430_1218.py b/InvenTree/stock/migrations/0002_auto_20180430_1218.py deleted file mode 100644 index a3ed70090f..0000000000 --- a/InvenTree/stock/migrations/0002_auto_20180430_1218.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.12 on 2018-04-30 12:18 -from __future__ import unicode_literals - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='quantity', - field=models.PositiveIntegerField(default=1, validators=[django.core.validators.MinValueValidator(0)]), - ), - ] diff --git a/InvenTree/stock/migrations/0003_auto_20180510_1042.py b/InvenTree/stock/migrations/0003_auto_20180510_1042.py deleted file mode 100644 index dfa34c6b5c..0000000000 --- a/InvenTree/stock/migrations/0003_auto_20180510_1042.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.12 on 2018-05-10 10:42 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0002_auto_20180430_1218'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitemtracking', - name='date', - field=models.DateTimeField(auto_now_add=True), - ), - ] diff --git a/InvenTree/stock/migrations/0004_auto_20190412_2030.py b/InvenTree/stock/migrations/0004_auto_20190412_2030.py deleted file mode 100644 index d55dbc6fff..0000000000 --- a/InvenTree/stock/migrations/0004_auto_20190412_2030.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-04-12 10:30 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0003_auto_20180510_1042'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='status', - field=models.PositiveIntegerField(choices=[(10, 'OK'), (50, 'Attention needed'), (55, 'Damaged'), (60, 'Destroyed')], default=10, validators=[django.core.validators.MinValueValidator(0)]), - ), - ] diff --git a/InvenTree/stock/migrations/0005_stockitemtracking_quantity.py b/InvenTree/stock/migrations/0005_stockitemtracking_quantity.py deleted file mode 100644 index f654486075..0000000000 --- a/InvenTree/stock/migrations/0005_stockitemtracking_quantity.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-04-12 14:09 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0004_auto_20190412_2030'), - ] - - operations = [ - migrations.AddField( - model_name='stockitemtracking', - name='quantity', - field=models.PositiveIntegerField(default=1, validators=[django.core.validators.MinValueValidator(0)]), - ), - ] diff --git a/InvenTree/stock/migrations/0006_stockitem_uuid.py b/InvenTree/stock/migrations/0006_stockitem_uuid.py deleted file mode 100644 index 4d358e7201..0000000000 --- a/InvenTree/stock/migrations/0006_stockitem_uuid.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-04-12 15:06 - -from django.db import migrations, models -import uuid - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0005_stockitemtracking_quantity'), - ] - - operations = [ - migrations.AddField( - model_name='stockitem', - name='uuid', - field=models.UUIDField(blank=True, default=uuid.uuid4, editable=False), - ), - ] diff --git a/InvenTree/stock/migrations/0007_auto_20190417_1812.py b/InvenTree/stock/migrations/0007_auto_20190417_1812.py deleted file mode 100644 index 8b9ec638e3..0000000000 --- a/InvenTree/stock/migrations/0007_auto_20190417_1812.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2 on 2019-04-17 08:12 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0006_stockitem_uuid'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='notes', - field=models.CharField(blank=True, help_text='Stock Item Notes', max_length=250), - ), - ] diff --git a/InvenTree/stock/migrations/0008_auto_20190417_1819.py b/InvenTree/stock/migrations/0008_auto_20190417_1819.py deleted file mode 100644 index 46659abd66..0000000000 --- a/InvenTree/stock/migrations/0008_auto_20190417_1819.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 2.2 on 2019-04-17 08:19 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0007_auto_20190417_1812'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='stocktake_user', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stocktake_stock', to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/InvenTree/stock/migrations/0009_auto_20190428_0841.py b/InvenTree/stock/migrations/0009_auto_20190428_0841.py deleted file mode 100644 index 4e22dad76b..0000000000 --- a/InvenTree/stock/migrations/0009_auto_20190428_0841.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-04-27 22:41 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0008_auto_20190417_1819'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='location', - field=models.ForeignKey(blank=True, help_text='Where is this stock item located?', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='stock_items', to='stock.StockLocation'), - ), - ] diff --git a/InvenTree/stock/migrations/0010_auto_20190501_2344.py b/InvenTree/stock/migrations/0010_auto_20190501_2344.py deleted file mode 100644 index 61ea730b03..0000000000 --- a/InvenTree/stock/migrations/0010_auto_20190501_2344.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2 on 2019-05-01 13:44 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0009_auto_20190428_0841'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='batch', - field=models.CharField(blank=True, help_text='Batch code for this stock item', max_length=100, null=True), - ), - ] diff --git a/InvenTree/stock/migrations/0011_auto_20190502_0041.py b/InvenTree/stock/migrations/0011_auto_20190502_0041.py deleted file mode 100644 index b540421de5..0000000000 --- a/InvenTree/stock/migrations/0011_auto_20190502_0041.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-05-01 14:41 - -from django.db import migrations, models -import uuid - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0010_auto_20190501_2344'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='uuid', - field=models.UUIDField(blank=True, default=uuid.uuid4, editable=False, help_text='Unique ID for the StockItem'), - ), - ] diff --git a/InvenTree/stock/migrations/0012_auto_20190502_0058.py b/InvenTree/stock/migrations/0012_auto_20190502_0058.py deleted file mode 100644 index 1c86926fcd..0000000000 --- a/InvenTree/stock/migrations/0012_auto_20190502_0058.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-05-01 14:58 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0011_auto_20190502_0041'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='part', - field=models.ForeignKey(help_text='Base part', on_delete=django.db.models.deletion.CASCADE, related_name='locations', to='part.Part'), - ), - ] diff --git a/InvenTree/stock/migrations/0013_remove_stockitem_uuid.py b/InvenTree/stock/migrations/0013_remove_stockitem_uuid.py deleted file mode 100644 index 79184862ed..0000000000 --- a/InvenTree/stock/migrations/0013_remove_stockitem_uuid.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 2.2 on 2019-05-02 10:39 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0012_auto_20190502_0058'), - ] - - operations = [ - migrations.RemoveField( - model_name='stockitem', - name='uuid', - ), - ] diff --git a/InvenTree/stock/migrations/0014_auto_20190508_2332.py b/InvenTree/stock/migrations/0014_auto_20190508_2332.py deleted file mode 100644 index 829bd34df8..0000000000 --- a/InvenTree/stock/migrations/0014_auto_20190508_2332.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-05-08 13:32 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0013_remove_stockitem_uuid'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='supplier_part', - field=models.ForeignKey(blank=True, help_text='Select a matching supplier part for this stock item', null=True, on_delete=django.db.models.deletion.SET_NULL, to='part.SupplierPart'), - ), - ] diff --git a/InvenTree/stock/migrations/0015_stockitem_delete_on_deplete.py b/InvenTree/stock/migrations/0015_stockitem_delete_on_deplete.py deleted file mode 100644 index 29631b94d8..0000000000 --- a/InvenTree/stock/migrations/0015_stockitem_delete_on_deplete.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2 on 2019-05-09 12:59 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0014_auto_20190508_2332'), - ] - - operations = [ - migrations.AddField( - model_name='stockitem', - name='delete_on_deplete', - field=models.BooleanField(default=True, help_text='Delete this Stock Item when stock is depleted'), - ), - ] diff --git a/InvenTree/stock/migrations/0016_auto_20190512_2119.py b/InvenTree/stock/migrations/0016_auto_20190512_2119.py deleted file mode 100644 index 582e68d277..0000000000 --- a/InvenTree/stock/migrations/0016_auto_20190512_2119.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2 on 2019-05-12 11:19 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0015_stockitem_delete_on_deplete'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='updated', - field=models.DateField(auto_now=True, null=True), - ), - ] diff --git a/InvenTree/stock/migrations/0017_auto_20190518_1759.py b/InvenTree/stock/migrations/0017_auto_20190518_1759.py deleted file mode 100644 index c0e583999e..0000000000 --- a/InvenTree/stock/migrations/0017_auto_20190518_1759.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-05-18 07:59 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0016_auto_20190512_2119'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='supplier_part', - field=models.ForeignKey(blank=True, help_text='Select a matching supplier part for this stock item', null=True, on_delete=django.db.models.deletion.SET_NULL, to='company.SupplierPart'), - ), - ] diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 04e814707e..fcb1622f27 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -22,7 +22,6 @@ from InvenTree import helpers from InvenTree.models import InvenTreeTree from part.models import Part -from company.models import SupplierPart class StockLocation(InvenTreeTree):