Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Adding store stock models and template tag #8

Merged
merged 4 commits into from

2 participants

@a-musing-moose

Mostly about store stock.

Adding a STORES_SRID to globally set this for geometry field types. Mostly so I can set it to some stupid flat projection for tests against spatialite.

Also added a fix to the make file as the geo ip data was downloading to the wrong place for some reason

stores/managers.py
@@ -1,3 +1,4 @@
+from django.db import models

This doesn't seem to be used in the file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
stores/models.py
@@ -127,3 +131,37 @@ class Meta:
ordering = ['weekday']
verbose_name = _("Opening period")
verbose_name_plural = _("Opening periods")
+
+
+class StoreStock(models.Model):
+
+ store = models.ForeignKey('stores.Store',
+ related_name='stock')
+ product = models.ForeignKey('catalogue.Product', related_name="store_stock")

The two foreign keys are missing a translatable verbose_name keyword. Would you mind adding those?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@elbaschid

Thanks, it looks good. I added a few comments on minor style issues. Also would you mind checking that the lines are max 79 characters as defined in PEP8? Sorry for being pedantic.

@elbaschid elbaschid merged commit 899a620 into django-oscar:master
@elbaschid

Great, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
3  Makefile
@@ -1,10 +1,9 @@
.PHONY: sandbox geoip
geoip:
- cd sandbox/geoip
wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz
gunzip GeoLiteCity.dat.gz
- cd -
+ mv GeoLiteCity.dat sandbox/geoip
sandbox:
rm sandbox/sandbox/sandbox.sqlite3 || true
View
1  docs/index.rst
@@ -11,6 +11,7 @@ Contents:
.. toctree::
:maxdepth: 2
+ settings
Indices and tables
View
9 docs/settings.rst
@@ -0,0 +1,9 @@
+Settings
+========
+
+STORES_SRID
+-----------
+
+Default: ``4326`` (i.e. WGS 84)
+
+This sets the SRID for the stores location field.
View
1  runtests.py
@@ -72,6 +72,7 @@ def configure():
},
GEOIP_PATH = 'sandbox/geoip',
NOSE_ARGS=['-s', '-x', '--with-spec'],
+ STORES_SRID=32140, # Flat projection so spatialite can do distances
**OSCAR_SETTINGS
)
View
BIN  sandbox/sandbox/__init__.pyc
Binary file not shown
View
2  sandbox/sandbox/settings.py
@@ -190,3 +190,5 @@
},
}
}
+
+STORES_SRID = 32140, # Flat projection so spatialite can do distances
View
BIN  sandbox/sandbox/wsgi.pyc
Binary file not shown
View
6 stores/admin.py
@@ -3,6 +3,10 @@
from stores import models
-admin.site.register(models.Store)
+class StoreAdmin(admin.ModelAdmin):
+ prepopulated_fields = {"slug": ("name",)}
+
+admin.site.register(models.Store, StoreAdmin)
admin.site.register(models.StoreGroup)
admin.site.register(models.OpeningPeriod)
+admin.site.register(models.StoreStock)
View
12 stores/app.py
@@ -14,8 +14,16 @@ def get_urls(self):
urlpatterns = super(StoresApplication, self).get_urls()
urlpatterns += patterns('',
- url(r'^$', self.list_view.as_view(), name='index'),
- url(r'^(?P<slug>[\w-]+)/$', self.detail_view.as_view(), name='detail'),
+ url(
+ r'^$',
+ self.list_view.as_view(),
+ name='index'
+ ),
+ url(
+ r'^(?P<slug>[\w-]+)/$',
+ self.detail_view.as_view(),
+ name='detail'
+ ),
)
return self.post_process_urls(urlpatterns)
View
6 stores/dashboard/app.py
@@ -26,7 +26,11 @@ class StoresDashboardApplication(Application):
def get_urls(self):
urlpatterns = patterns('',
- url(r'^$', self.store_list_view.as_view(), name='store-list'),
+ url(
+ r'^$',
+ self.store_list_view.as_view(),
+ name='store-list'
+ ),
url(
r'^create/$',
self.store_create_view.as_view(),
View
8 stores/dashboard/forms.py
@@ -46,6 +46,10 @@ class Meta:
'name': forms.TextInput(
attrs={'placeholder': _("e.g. Christmas")}
),
- 'start': forms.TextInput(attrs={'placeholder': _("e.g. 9am, noon, etc.")}),
- 'end': forms.TextInput(attrs={'placeholder': _("e.g. 5pm, late, etc.")}),
+ 'start': forms.TextInput(
+ attrs={'placeholder': _("e.g. 9am, noon, etc.")}
+ ),
+ 'end': forms.TextInput(
+ attrs={'placeholder': _("e.g. 5pm, late, etc.")}
+ ),
}
View
1  stores/managers.py
@@ -15,4 +15,3 @@ def get_query_set(self):
def pickup_stores(self):
return self.get_query_set().pickup_stores()
-
View
216 stores/migrations/0002_auto__add_storestock.py
@@ -0,0 +1,216 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding model 'StoreStock'
+ db.create_table('stores_storestock', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ (u'Store', self.gf('django.db.models.fields.related.ForeignKey')(related_name='stock', to=orm['stores.Store'])),
+ ('product', self.gf('django.db.models.fields.related.OneToOneField')(related_name='store_stock', unique=True, to=orm['catalogue.Product'])),
+ ('num_in_stock', self.gf('django.db.models.fields.PositiveIntegerField')(default=0, null=True, blank=True)),
+ ('num_allocated', self.gf('django.db.models.fields.IntegerField')(default=0, null=True, blank=True)),
+ ('location', self.gf('django.db.models.fields.CharField')(max_length=50, null=True, blank=True)),
+ ('date_created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
+ ('date_updated', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, db_index=True, blank=True)),
+ ))
+ db.send_create_signal('stores', ['StoreStock'])
+
+
+ def backwards(self, orm):
+ # Deleting model 'StoreStock'
+ db.delete_table('stores_storestock')
+
+
+ models = {
+ 'address.country': {
+ 'Meta': {'ordering': "('-is_highlighted', 'name')", 'object_name': 'Country'},
+ 'is_highlighted': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'is_shipping_country': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'iso_3166_1_a2': ('django.db.models.fields.CharField', [], {'max_length': '2', 'primary_key': 'True'}),
+ 'iso_3166_1_a3': ('django.db.models.fields.CharField', [], {'max_length': '3', 'null': 'True', 'db_index': 'True'}),
+ 'iso_3166_1_numeric': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'db_index': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'printable_name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'catalogue.attributeentity': {
+ 'Meta': {'object_name': 'AttributeEntity'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'blank': 'True'}),
+ 'type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'entities'", 'to': "orm['catalogue.AttributeEntityType']"})
+ },
+ 'catalogue.attributeentitytype': {
+ 'Meta': {'object_name': 'AttributeEntityType'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'blank': 'True'})
+ },
+ 'catalogue.attributeoption': {
+ 'Meta': {'object_name': 'AttributeOption'},
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'options'", 'to': "orm['catalogue.AttributeOptionGroup']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'option': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'catalogue.attributeoptiongroup': {
+ 'Meta': {'object_name': 'AttributeOptionGroup'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
+ },
+ 'catalogue.category': {
+ 'Meta': {'ordering': "['full_name']", 'object_name': 'Category'},
+ 'depth': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'full_name': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'db_index': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'numchild': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '1024'})
+ },
+ 'catalogue.option': {
+ 'Meta': {'object_name': 'Option'},
+ 'code': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'type': ('django.db.models.fields.CharField', [], {'default': "'Required'", 'max_length': '128'})
+ },
+ 'catalogue.product': {
+ 'Meta': {'ordering': "['-date_created']", 'object_name': 'Product'},
+ 'attributes': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['catalogue.ProductAttribute']", 'through': "orm['catalogue.ProductAttributeValue']", 'symmetrical': 'False'}),
+ 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['catalogue.Category']", 'through': "orm['catalogue.ProductCategory']", 'symmetrical': 'False'}),
+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'date_updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
+ 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_discountable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'variants'", 'null': 'True', 'to': "orm['catalogue.Product']"}),
+ 'product_class': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ProductClass']", 'null': 'True'}),
+ 'product_options': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['catalogue.Option']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'recommended_products': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['catalogue.Product']", 'symmetrical': 'False', 'through': "orm['catalogue.ProductRecommendation']", 'blank': 'True'}),
+ 'related_products': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'relations'", 'blank': 'True', 'to': "orm['catalogue.Product']"}),
+ 'score': ('django.db.models.fields.FloatField', [], {'default': '0.0', 'db_index': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255'}),
+ 'status': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'null': 'True', 'blank': 'True'}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'upc': ('django.db.models.fields.CharField', [], {'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'})
+ },
+ 'catalogue.productattribute': {
+ 'Meta': {'ordering': "['code']", 'object_name': 'ProductAttribute'},
+ 'code': ('django.db.models.fields.SlugField', [], {'max_length': '128'}),
+ 'entity_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.AttributeEntityType']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'option_group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.AttributeOptionGroup']", 'null': 'True', 'blank': 'True'}),
+ 'product_class': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'attributes'", 'null': 'True', 'to': "orm['catalogue.ProductClass']"}),
+ 'required': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'type': ('django.db.models.fields.CharField', [], {'default': "'text'", 'max_length': '20'})
+ },
+ 'catalogue.productattributevalue': {
+ 'Meta': {'object_name': 'ProductAttributeValue'},
+ 'attribute': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ProductAttribute']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'product': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'attribute_values'", 'to': "orm['catalogue.Product']"}),
+ 'value_boolean': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'value_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'value_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.AttributeEntity']", 'null': 'True', 'blank': 'True'}),
+ 'value_float': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
+ 'value_integer': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+ 'value_option': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.AttributeOption']", 'null': 'True', 'blank': 'True'}),
+ 'value_richtext': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'value_text': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'})
+ },
+ 'catalogue.productcategory': {
+ 'Meta': {'ordering': "['-is_canonical']", 'object_name': 'ProductCategory'},
+ 'category': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Category']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_canonical': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+ 'product': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Product']"})
+ },
+ 'catalogue.productclass': {
+ 'Meta': {'ordering': "['name']", 'object_name': 'ProductClass'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'options': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['catalogue.Option']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'requires_shipping': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128'}),
+ 'track_stock': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ 'catalogue.productrecommendation': {
+ 'Meta': {'object_name': 'ProductRecommendation'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'primary': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'primary_recommendations'", 'to': "orm['catalogue.Product']"}),
+ 'ranking': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '0'}),
+ 'recommendation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Product']"})
+ },
+ 'stores.openingperiod': {
+ 'Meta': {'ordering': "['weekday']", 'object_name': 'OpeningPeriod'},
+ u'Store': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'opening_periods'", 'to': "orm['stores.Store']"}),
+ 'end': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'start': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}),
+ 'weekday': ('django.db.models.fields.PositiveIntegerField', [], {})
+ },
+ 'stores.store': {
+ u'Group': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'stores'", 'null': 'True', 'to': "orm['stores.StoreGroup']"}),
+ 'Meta': {'object_name': 'Store'},
+ 'description': ('django.db.models.fields.CharField', [], {'max_length': '2000', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_pickup_store': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'location': ('django.contrib.gis.db.models.fields.PointField', [], {'null': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'reference': ('django.db.models.fields.CharField', [], {'max_length': '32', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '100', 'unique': 'True', 'null': 'True'})
+ },
+ 'stores.storeaddress': {
+ 'Meta': {'object_name': 'StoreAddress'},
+ 'country': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['address.Country']"}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+ 'line1': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'line2': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'line3': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'line4': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'postcode': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+ 'search_text': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),
+ 'state': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'store': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'address'", 'unique': 'True', 'to': "orm['stores.Store']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'})
+ },
+ 'stores.storecontact': {
+ 'Meta': {'object_name': 'StoreContact'},
+ u'Store': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'contact_details'", 'unique': 'True', 'to': "orm['stores.Store']"}),
+ 'email': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'manager_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
+ 'phone': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'})
+ },
+ 'stores.storegroup': {
+ 'Meta': {'object_name': 'StoreGroup'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100', 'blank': 'True'})
+ },
+ 'stores.storestock': {
+ 'Meta': {'object_name': 'StoreStock'},
+ u'Store': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'stock'", 'to': "orm['stores.Store']"}),
+ 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'date_updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'num_allocated': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'}),
+ 'num_in_stock': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'}),
+ 'product': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'store_stock'", 'unique': 'True', 'to': "orm['catalogue.Product']"})
+ }
+ }
+
+ complete_apps = ['stores']
View
96 stores/models.py
@@ -3,11 +3,16 @@
from django.utils.translation import ugettext as _
from django.template.defaultfilters import slugify
from django.contrib.gis.db.models import PointField
+from django.contrib.gis.db.models import GeoManager
+from django.conf import settings
+
from oscar.apps.address.abstract_models import AbstractAddress
from stores.managers import StoreManager
+STORES_SRID = getattr(settings, 'STORES_SRID', 4326)
+
class StoreAddress(AbstractAddress):
store = models.OneToOneField(
@@ -36,7 +41,7 @@ class StoreContact(models.Model):
phone = models.CharField(_('Phone'), max_length=20, blank=True, null=True)
email = models.CharField(_('Email'), max_length=100, blank=True, null=True)
- store = models.OneToOneField('stores.Store', name=_("Store"),
+ store = models.OneToOneField('stores.Store', verbose_name=_("Store"),
related_name="contact_details")
def __unicode__(self):
@@ -64,10 +69,15 @@ class Store(models.Model):
blank=True, null=True
)
- location = PointField(_("Location"), null=True, blank=True)
+ location = PointField(
+ _("Location"),
+ null=True,
+ blank=True,
+ srid=STORES_SRID
+ )
group = models.ForeignKey('stores.StoreGroup', related_name='stores',
- name=_("Group"), null=True, blank=True)
+ verbose_name=_("Group"), null=True, blank=True)
is_pickup_store = models.BooleanField(_("Is pickup store"), default=True)
is_active = models.BooleanField(_("Is active"), default=True)
@@ -95,18 +105,26 @@ class OpeningPeriod(models.Model):
SATURDAY: _("Saturday"),
SUNDAY: _("Sunday"),
}
- store = models.ForeignKey('stores.Store', name=_("Store"),
+ store = models.ForeignKey('stores.Store', verbose_name=_("Store"),
related_name='opening_periods')
weekday_choices = [(k, v) for k, v in WEEK_DAYS.items()]
- weekday = models.PositiveIntegerField(_("Weekday"),
- choices=weekday_choices)
+ weekday = models.PositiveIntegerField(
+ _("Weekday"),
+ choices=weekday_choices
+ )
start = models.CharField(
- _("Start"), max_length=30, null=True, blank=True,
+ _("Start"),
+ max_length=30,
+ null=True,
+ blank=True,
help_text=_("Leaving start and end time empty is displayed as 'Closed'")
)
end = models.CharField(
- _("End"), max_length=30, null=True, blank=True,
+ _("End"),
+ max_length=30,
+ null=True,
+ blank=True,
help_text=_("Leaving start and end time empty is displayed as 'Closed'")
)
@@ -127,3 +145,65 @@ class Meta:
ordering = ['weekday']
verbose_name = _("Opening period")
verbose_name_plural = _("Opening periods")
+
+
+class StoreStock(models.Model):
+
+ store = models.ForeignKey(
+ 'stores.Store',
+ verbose_name=_("Store"),
+ related_name='stock'
+ )
+ product = models.ForeignKey(
+ 'catalogue.Product',
+ verbose_name=_("Product"),
+ related_name="store_stock"
+ )
+ # Stock level information
+ num_in_stock = models.PositiveIntegerField(
+ _("Number in stock"),
+ default=0,
+ blank=True,
+ null=True
+ )
+
+ # The amount of stock allocated in store but not fed back to the master
+ num_allocated = models.IntegerField(
+ _("Number allocated"),
+ default=0,
+ blank=True,
+ null=True
+ )
+
+ location = models.CharField(
+ _("In store location"),
+ max_length=50,
+ blank=True,
+ null=True
+ )
+
+ # Date information
+ date_created = models.DateTimeField(
+ _("Date Created"),
+ auto_now_add=True
+ )
+ date_updated = models.DateTimeField(
+ _("Date Updated"),
+ auto_now=True,
+ db_index=True
+ )
+
+ class Meta:
+ verbose_name = _("Store Stock Record")
+ verbose_name_plural = _("Store Stock Records")
+
+ objects = GeoManager() # Needed for distance queries against stores
+
+ def __unicode__(self):
+ if self.store and self.product:
+ return "%s @ %s" % (self.product.title, self.store.name)
+ return "Store Stock"
+
+ @property
+ def is_available_to_buy(self):
+ return self.num_in_stock > self.num_allocated
View
0  stores/templatetags/__init__.py
No changes.
View
19 stores/templatetags/store_stock.py
@@ -0,0 +1,19 @@
+from django import template
+from django.db.models import get_model
+
+StoreStock = get_model('stores', 'StoreStock')
+
+register = template.Library()
+
+
+@register.assignment_tag
+def store_stock_for_product(product, location=None, limit=20):
+ query_set = StoreStock.objects.filter(product=product)
+ if location:
+ query_set = query_set.distance(
+ location,
+ field_name='store__location'
+ ).order_by('distance')
+ else:
+ query_set = query_set.order_by('store__name')
+ return query_set[0:limit]
View
62 tests/unit/templatetag_tests.py
@@ -0,0 +1,62 @@
+from django.test import TestCase
+from django.contrib.gis.geos.point import Point
+from django.template import Template, Context
+from django_dynamic_fixture import get as G
+
+from django.db.models import get_model
+
+Product = get_model('catalogue', 'Product')
+Store = get_model('stores', 'Store')
+StoreStock = get_model('stores', 'StoreStock')
+
+
+class StoreStockTest(TestCase):
+
+ def setUp(self):
+ self.product = G(Product)
+ self.store1_location = '{"type": "Point", "coordinates": [87.39,12.02]}'
+ self.store2_location = '{"type": "Point", "coordinates": [88.39,11.02]}'
+ self.store1 = G(Store, is_pickup_store=True, location=self.store1_location)
+ self.store2 = G(Store, is_pickup_store=True, location=self.store2_location)
+ self.store_stock1 = G(StoreStock, store=self.store1, product=self.product)
+ self.store_stock1 = G(StoreStock, store=self.store2, product=self.product)
+
+ def test_store_stock_loads(self):
+ rendered = Template(
+ '{% load store_stock %}'
+ ).render(Context())
+
+ def test_store_stock_for_product_returns_stock_lines(self):
+ rendered = Template(
+ """
+ {% load store_stock %} {% store_stock_for_product product as store_stock %}
+ {% for stock in store_stock %} {{ stock.store.name }} {% endfor %}
+ """
+ ).render(Context({
+ 'product': self.product
+ }))
+ self.assertTrue(self.store1.name in rendered)
+ self.assertTrue(self.store2.name in rendered)
+
+ def test_store_stock_for_product_limits_when_asked(self):
+ rendered = Template(
+ """
+ {% load store_stock %} {% store_stock_for_product product limit=1 as store_stock %}
+ {% for stock in store_stock %} {{ stock.store.name }} {% endfor %}
+ """
+ ).render(Context({
+ 'product': self.product
+ }))
+ self.assertTrue(self.store1.name in rendered)
+
+ def test_store_stock_for_product_order_by_closed(self):
+ rendered = Template(
+ """
+ {% load store_stock %} {% store_stock_for_product product location=loc as store_stock %}
+ {% for stock in store_stock %}{{ stock.store.name }}{% endfor %}
+ """
+ ).render(Context({
+ 'product': self.product,
+ 'loc': '{"type": "Point", "coordinates": [88.39,11.02]}'
+ }))
+ self.assertTrue("%s%s" % (self.store2.name, self.store1.name) in rendered)
Something went wrong with that request. Please try again.