diff --git a/CHANGELOG.md b/CHANGELOG.md index cd3e7ba..8c832b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file. +## [0.6.5] +- Fix Issue with group_by field pointing to model with custom primary key Issue #58 + + ## [0.6.4] - Fix highchart cache to target the specific chart - Added initial and required to report_form_factory diff --git a/docs/source/conf.py b/docs/source/conf.py index f358cdd..3462e81 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -27,7 +27,7 @@ master_doc = 'index' # The full version, including alpha/beta/rc tags -release = '0.6.4' +release = '0.6.5' # -- General configuration --------------------------------------------------- diff --git a/slick_reporting/__init__.py b/slick_reporting/__init__.py index b012eb3..1c1030f 100644 --- a/slick_reporting/__init__.py +++ b/slick_reporting/__init__.py @@ -1,5 +1,5 @@ default_app_config = 'slick_reporting.apps.ReportAppConfig' -VERSION = (0, 6, 4) +VERSION = (0, 6, 5) -__version__ = '0.6.4' +__version__ = '0.6.5' diff --git a/slick_reporting/generator.py b/slick_reporting/generator.py index 2b597ae..4c169b3 100644 --- a/slick_reporting/generator.py +++ b/slick_reporting/generator.py @@ -206,14 +206,12 @@ def __init__(self, report_model=None, main_queryset=None, start_date=None, end_d except IndexError: raise ImproperlyConfigured( f'Can not find group_by field:{self.group_by} in report_model {self.report_model} ') - self.focus_field_as_key = self.group_by_field if '__' not in self.group_by: self.group_by_field_attname = self.group_by_field.attname else: self.group_by_field_attname = self.group_by else: - self.focus_field_as_key = None self.group_by_field_attname = None # doc_types = form.get_doc_type_plus_minus_lists() @@ -340,6 +338,13 @@ def _prepare_report_dependencies(self): report_class.init_preparation(q_filters, date_filter) self.report_fields_classes[name] = report_class + @staticmethod + def get_primary_key_name(model): + for field in model._meta.fields: + if field.primary_key: + return field.attname + return '' + def _get_record_data(self, obj, columns): """ the function is run for every obj in the main_queryset @@ -348,12 +353,15 @@ def _get_record_data(self, obj, columns): :return: a dict object containing all needed data """ - # todo , if columns are empty for whatever reason this will throw an error - display_link = self.list_display_links or columns[0] data = {} group_by_val = None if self.group_by: - column_data = obj.get(self.group_by_field_attname, obj.get('id')) + if self.group_by_field.related_model and '__' not in self.group_by: + primary_key_name = self.get_primary_key_name(self.group_by_field.related_model) + else: + primary_key_name = self.group_by_field_attname + + column_data = obj.get(primary_key_name, obj.get('id')) group_by_val = str(column_data) for window, window_cols in columns: @@ -379,8 +387,6 @@ def _get_record_data(self, obj, columns): else: data[name] = obj.get(name, '') - # if self.group_by and name in display_link: - # data[name] = make_linkable_field(self.group_by_field.related_model, group_by_val, data[name]) return data def get_report_data(self): diff --git a/tests/models.py b/tests/models.py index 599346e..a36c11b 100644 --- a/tests/models.py +++ b/tests/models.py @@ -23,6 +23,25 @@ class Meta: verbose_name_plural = _('Products') +class ProductCustomID(models.Model): + CATEGORY_CHOICES = ( + ('tiny', 'tiny'), + ('small', 'small'), + ('medium', 'medium'), + ('big', 'big'), + ) + hash = models.AutoField(primary_key=True) + slug = models.CharField(max_length=200, verbose_name=_('Slug')) + name = models.CharField(max_length=200, verbose_name=_('Name')) + sku = models.CharField(max_length=200, default='', blank=True) + category = models.CharField(max_length=10, choices=CATEGORY_CHOICES) + notes = models.TextField() + + class Meta: + verbose_name = _('Product') + verbose_name_plural = _('Products') + + class Contact(models.Model): address = models.CharField(max_length=200, verbose_name=_('Name')) @@ -65,6 +84,31 @@ class Meta: ordering = ['-created_at'] +class SalesProductWithCustomID(models.Model): + slug = models.SlugField() + doc_date = models.DateTimeField(_('date'), db_index=True) + client = models.ForeignKey(Client, on_delete=models.CASCADE) + product = models.ForeignKey(ProductCustomID, on_delete=models.CASCADE) + quantity = models.DecimalField(_('quantity'), max_digits=19, decimal_places=2, default=0) + price = models.DecimalField(_('price'), max_digits=19, decimal_places=2, default=0) + value = models.DecimalField(_('value'), max_digits=19, decimal_places=2, default=0) + created_at = models.DateTimeField(null=True, verbose_name=_('Created at')) + flag = models.CharField(max_length=50, default='sales') + + content_type = models.ForeignKey(ContentType, on_delete=models.DO_NOTHING, null=True) + object_id = models.PositiveIntegerField(null=True) + content_object = GenericForeignKey('content_type', 'object_id') + + def save(self, force_insert=False, force_update=False, using=None, update_fields=None): + self.value = self.quantity * self.price + super().save(force_insert, force_update, using, update_fields) + + class Meta: + verbose_name = _('Sale') + verbose_name_plural = _('Sales') + ordering = ['-created_at'] + + class SalesWithFlag(models.Model): slug = models.SlugField() doc_date = models.DateTimeField(_('date'), db_index=True) diff --git a/tests/report_generators.py b/tests/report_generators.py index 4318e79..a48d527 100644 --- a/tests/report_generators.py +++ b/tests/report_generators.py @@ -5,7 +5,7 @@ from slick_reporting.fields import SlickReportField, PercentageToBalance from slick_reporting.generator import ReportGenerator -from .models import Client, SimpleSales, Product, SalesWithFlag +from .models import Client, SimpleSales, Product, SalesWithFlag, SalesProductWithCustomID from .models import OrderLine @@ -82,6 +82,13 @@ class ProductTotalSales(ReportGenerator): columns = ['slug', 'name', '__balance__', '__balance_quantity__'] +class ProductTotalSalesProductWithCustomID(ReportGenerator): + report_model = SalesProductWithCustomID + date_field = 'doc_date' + group_by = 'product' + columns = ['slug', 'name', '__balance__', '__balance_quantity__'] + + class ProductTotalSalesWithPercentage(ReportGenerator): report_model = SimpleSales date_field = 'doc_date' diff --git a/tests/tests.py b/tests/tests.py index af3028a..bf7d4a5 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -12,7 +12,8 @@ from tests.report_generators import ClientTotalBalance, ProductClientSalesMatrix2, GroupByCharField, \ GroupByCharFieldPlusTimeSeries, TimeSeriesWithOutGroupBy from . import report_generators -from .models import Client, Contact, Product, SimpleSales, UserJoined, SalesWithFlag, ComplexSales, TaxCode +from .models import Client, Contact, Product, SimpleSales, UserJoined, SalesWithFlag, ComplexSales, TaxCode, \ + ProductCustomID, SalesProductWithCustomID from .views import SlickReportView User = get_user_model() @@ -49,6 +50,9 @@ def setUpTestData(cls): cls.product2 = Product.objects.create(name='Product 2', category='medium') cls.product3 = Product.objects.create(name='Product 3', category='big') + cls.product_w_custom_id1 = ProductCustomID.objects.create(name='Product 1', category='small') + cls.product_w_custom_id2 = ProductCustomID.objects.create(name='Product 2', category='medium') + SimpleSales.objects.create( doc_date=datetime.datetime(year, 1, 2), client=cls.client1, product=cls.product1, quantity=10, price=10, created_at=datetime.datetime(year, 1, 5)) @@ -107,6 +111,18 @@ def setUpTestData(cls): sale3.tax.add(cls.tax1) sale4.tax.add(cls.tax2) + SalesProductWithCustomID.objects.create( + doc_date=datetime.datetime(year, 1, 2), client=cls.client1, + product=cls.product_w_custom_id1, quantity=10, price=10, created_at=datetime.datetime(year, 1, 5)) + SalesProductWithCustomID.objects.create( + doc_date=datetime.datetime(year, 2, 2), client=cls.client1, + product=cls.product_w_custom_id1, quantity=10, price=10, created_at=datetime.datetime(year, 2, 3)) + + SalesProductWithCustomID.objects.create( + doc_date=datetime.datetime(year, 3, 2), client=cls.client1, + product=cls.product_w_custom_id2, quantity=10, price=10, created_at=datetime.datetime(year, 3, 3)) + + # @override_settings(ROOT_URLCONF='reporting_tests.urls', RA_CACHE_REPORTS=False, USE_TZ=False) class ReportTest(BaseTestData, TestCase): @@ -118,6 +134,12 @@ def test_client_balance(self): self.assertEqual(data[0].get('__balance__'), 300, data[0]) def test_product_total_sales(self): + report = report_generators.ProductTotalSalesProductWithCustomID() + data = report.get_report_data() + self.assertEqual(data[0]['__balance__'], 200) + self.assertEqual(data[1]['__balance__'], 100) + + def test_product_total_sales_product_custom_id(self): report = report_generators.ProductTotalSales() data = report.get_report_data() self.assertEqual(data[0]['__balance__'], 1800)