19
+            model_name='product',
20
+            name='total',
21
+            field=models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True),
22
+        ),
23
+        migrations.AddField(
24
+            model_name='product',
25
+            name='vat',
26
+            field=models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True),
27
+        ),
28
+        migrations.AlterField(
29
+            model_name='product',
30
+            name='price',
31
+            field=models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True),
32
+        ),
33
+    ]

+ 40 - 0
app/fruit/migrations/0017_auto_20210807_1250.py

@@ -0,0 +1,40 @@
1
+# Generated by Django 3.2.6 on 2021-08-07 05:50
2
+
3
+from django.db import migrations, models
4
+
5
+
6
+class Migration(migrations.Migration):
7
+
8
+    dependencies = [
9
+        ('fruit', '0016_auto_20210807_1233'),
10
+    ]
11
+
12
+    operations = [
13
+        migrations.RemoveField(
14
+            model_name='product',
15
+            name='sub_total',
16
+        ),
17
+        migrations.RemoveField(
18
+            model_name='product',
19
+            name='total',
20
+        ),
21
+        migrations.RemoveField(
22
+            model_name='product',
23
+            name='vat',
24
+        ),
25
+        migrations.AddField(
26
+            model_name='sale',
27
+            name='sub_total',
28
+            field=models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True),
29
+        ),
30
+        migrations.AddField(
31
+            model_name='sale',
32
+            name='total',
33
+            field=models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True),
34
+        ),
35
+        migrations.AddField(
36
+            model_name='sale',
37
+            name='vat',
38
+            field=models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True),
39
+        ),
40
+    ]

+ 37 - 0
app/fruit/migrations/0018_inbox.py

@@ -0,0 +1,37 @@
1
+# Generated by Django 3.2.6 on 2021-08-07 08:21
2
+
3
+from django.conf import settings
4
+from django.db import migrations, models
5
+import django.db.models.deletion
6
+
7
+
8
+class Migration(migrations.Migration):
9
+
10
+    dependencies = [
11
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12
+        ('fruit', '0017_auto_20210807_1250'),
13
+    ]
14
+
15
+    operations = [
16
+        migrations.CreateModel(
17
+            name='Inbox',
18
+            fields=[
19
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20
+                ('created_at', models.DateTimeField(auto_now_add=True, null=True)),
21
+                ('updated_at', models.DateTimeField(auto_now=True)),
22
+                ('subject', models.CharField(max_length=200)),
23
+                ('body', models.TextField()),
24
+                ('tel', models.CharField(blank=True, max_length=100, null=True)),
25
+                ('line_id', models.CharField(blank=True, max_length=100, null=True)),
26
+                ('email', models.EmailField(blank=True, max_length=254, null=True)),
27
+                ('buyer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='fruit.buyer')),
28
+                ('created_by', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='inbox_created', to=settings.AUTH_USER_MODEL)),
29
+                ('modified_by', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='inbox_modified', to=settings.AUTH_USER_MODEL)),
30
+                ('product', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='fruit.product')),
31
+                ('store', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='fruit.store')),
32
+            ],
33
+            options={
34
+                'abstract': False,
35
+            },
36
+        ),
37
+    ]

+ 18 - 0
app/fruit/migrations/0019_inbox_status.py

@@ -0,0 +1,18 @@
1
+# Generated by Django 3.2.6 on 2021-08-07 08:27
2
+
3
+from django.db import migrations, models
4
+
5
+
6
+class Migration(migrations.Migration):
7
+
8
+    dependencies = [
9
+        ('fruit', '0018_inbox'),
10
+    ]
11
+
12
+    operations = [
13
+        migrations.AddField(
14
+            model_name='inbox',
15
+            name='status',
16
+            field=models.CharField(choices=[('request', 'Request'), ('read', 'Read'), ('process', 'Process'), ('complete', 'Complete')], default='request', max_length=30),
17
+        ),
18
+    ]

+ 67 - 0
app/fruit/migrations/0020_purchase_vendor.py

@@ -0,0 +1,67 @@
1
+# Generated by Django 3.2.6 on 2021-08-07 16:05
2
+
3
+from django.conf import settings
4
+from django.db import migrations, models
5
+import django.db.models.deletion
6
+import django_google_maps.fields
7
+
8
+
9
+class Migration(migrations.Migration):
10
+
11
+    dependencies = [
12
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
13
+        ('fruit', '0019_inbox_status'),
14
+    ]
15
+
16
+    operations = [
17
+        migrations.CreateModel(
18
+            name='Vendor',
19
+            fields=[
20
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
21
+                ('created_at', models.DateTimeField(auto_now_add=True, null=True)),
22
+                ('updated_at', models.DateTimeField(auto_now=True)),
23
+                ('name', models.CharField(max_length=200)),
24
+                ('code', models.CharField(max_length=200)),
25
+                ('description', models.TextField(blank=True, null=True)),
26
+                ('price', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
27
+                ('details', models.JSONField(blank=True, null=True)),
28
+                ('n_unit', models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True)),
29
+                ('unit_name', models.CharField(max_length=200, null=True)),
30
+                ('tel', models.CharField(blank=True, max_length=100, null=True)),
31
+                ('line_id', models.CharField(blank=True, max_length=100, null=True)),
32
+                ('email', models.EmailField(blank=True, max_length=254, null=True)),
33
+                ('address_text', models.TextField()),
34
+                ('address', django_google_maps.fields.AddressField(max_length=200)),
35
+                ('geolocation', django_google_maps.fields.GeoLocationField(max_length=100)),
36
+                ('created_by', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vendor_created', to=settings.AUTH_USER_MODEL)),
37
+                ('modified_by', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='vendor_modified', to=settings.AUTH_USER_MODEL)),
38
+                ('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fruit.product')),
39
+                ('store', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='fruit.store')),
40
+            ],
41
+            options={
42
+                'abstract': False,
43
+            },
44
+        ),
45
+        migrations.CreateModel(
46
+            name='Purchase',
47
+            fields=[
48
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
49
+                ('created_at', models.DateTimeField(auto_now_add=True, null=True)),
50
+                ('updated_at', models.DateTimeField(auto_now=True)),
51
+                ('price', models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True)),
52
+                ('n_unit', models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True)),
53
+                ('unit_name', models.CharField(max_length=200, null=True)),
54
+                ('sub_total', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
55
+                ('vat', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
56
+                ('total', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
57
+                ('created_by', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='purchase_created', to=settings.AUTH_USER_MODEL)),
58
+                ('modified_by', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='purchase_modified', to=settings.AUTH_USER_MODEL)),
59
+                ('product', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='fruit.product')),
60
+                ('store', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='fruit.store')),
61
+                ('vendor', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='fruit.vendor')),
62
+            ],
63
+            options={
64
+                'abstract': False,
65
+            },
66
+        ),
67
+    ]

+ 112 - 3
app/fruit/models.py

@@ -20,10 +20,11 @@ from django.contrib.auth.models import User
20 20
 from django.db.models.signals import post_save
21 21
 from django.dispatch import receiver
22 22
 from shaqfindbed.utils import get_current_user
23
-
24
-
23
+import decimal
25 24
 # Create your models here.
26 25
 
26
+VAT = 0.07
27
+
27 28
 class GenericModel(models.Model):
28 29
     created_at = models.DateTimeField(auto_now_add=True, null=True)
29 30
     updated_at = models.DateTimeField(auto_now=True)
@@ -90,22 +91,126 @@ class ProductType(MPTTModel):
90 91
         return f"{self.name}"
91 92
 
92 93
 
94
+class Buyer(GenericModel, models.Model):
95
+    name = models.CharField(max_length=300, null=False, blank=False)
96
+    address_text = models.TextField(blank=False, null=False)
97
+
98
+    address = map_fields.AddressField(max_length=200)
99
+    geolocation = map_fields.GeoLocationField(max_length=100)
100
+
101
+
102
+    tel = models.CharField(max_length=100, null=False, blank=False)
103
+    line_id = models.CharField(max_length=100, null=True, blank=True)
104
+    email = models.EmailField(null=True, blank=True)
105
+
106
+
107
+    def __str__(self):
108
+        return f"{self.name} Tel.:{self.tel} Email:{self.email}"
109
+
110
+class Inbox(GenericModel, models.Model):
111
+    store = models.ForeignKey('Store', on_delete=models.DO_NOTHING, null=False, blank=False)
112
+    product = models.ForeignKey('Product', on_delete=models.DO_NOTHING, null=False, blank=False)
113
+    buyer = models.ForeignKey('Buyer', on_delete=models.DO_NOTHING, null=True, blank=True)
114
+
115
+    subject = models.CharField(max_length=200)
116
+    body = models.TextField(blank=False)
117
+
118
+    tel = models.CharField(max_length=100, null=True, blank=True)
119
+    line_id = models.CharField(max_length=100, null=True, blank=True)
120
+    email = models.EmailField(null=True, blank=True)
121
+
122
+
123
+    status = models.CharField(
124
+        max_length=30,
125
+        choices=(("request", "Request"), ("read", "Read"), ("process", "Process"), ("complete", "Complete")),
126
+        default="request",
127
+    )
128
+
129
+class Purchase(GenericModel, models.Model):
130
+    product = models.ForeignKey('Product', on_delete=models.DO_NOTHING, null=False, blank=False)
131
+    store = models.ForeignKey('Store', on_delete=models.DO_NOTHING, null=False, blank=False)
132
+    vendor = models.ForeignKey('Vendor', on_delete=models.DO_NOTHING, null=False, blank=False)
133
+
134
+    price = models.DecimalField(null=True, blank=True, decimal_places=2, max_digits=7)
135
+    n_unit = models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True)
136
+    unit_name = models.CharField(max_length=200, null=True)
137
+
138
+
139
+    sub_total = models.DecimalField(null=True, blank=True, decimal_places=2, max_digits=10)
140
+    vat = models.DecimalField(null=True, blank=True, decimal_places=2, max_digits=10)
141
+    total = models.DecimalField(null=True, blank=True, decimal_places=2, max_digits=10)
142
+
143
+    def save(self, *args, **kwargs):
144
+        self.sub_total = self.price * self.n_unit
145
+        self.vat  = self.sub_total * decimal.Decimal(VAT)
146
+        self.total = self.sub_total + self.vat
147
+        super(Purchase, self).save(*args, **kwargs)
148
+
149
+class Sale(GenericModel, models.Model):
150
+    product = models.ForeignKey('Product', on_delete=models.DO_NOTHING, null=False, blank=False)
151
+    store = models.ForeignKey('Store', on_delete=models.DO_NOTHING, null=False, blank=False)
152
+    sku = models.ForeignKey('ProductSKU', on_delete=models.DO_NOTHING, null=False, blank=False)
153
+    price = models.DecimalField(null=True, blank=True, decimal_places=2, max_digits=7)
154
+    n_unit = models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True)
155
+    unit_name = models.CharField(max_length=200, null=True)
156
+    buyer = models.ForeignKey('Buyer', on_delete=models.DO_NOTHING, null=False, blank=False)
157
+
158
+
159
+    sub_total = models.DecimalField(null=True, blank=True, decimal_places=2, max_digits=10)
160
+    vat = models.DecimalField(null=True, blank=True, decimal_places=2, max_digits=10)
161
+    total = models.DecimalField(null=True, blank=True, decimal_places=2, max_digits=10)
162
+
163
+    def save(self, *args, **kwargs):
164
+        self.sub_total = self.price * self.n_unit
165
+        self.vat  = self.sub_total * decimal.Decimal(VAT)
166
+        self.total = self.sub_total + self.vat
167
+        super(Sale, self).save(*args, **kwargs)
168
+
93 169
 class Product(GenericModel, models.Model ):
94 170
     name = models.CharField(max_length=200)
95 171
     code = models.CharField(max_length=200)
96 172
     product_type = TreeForeignKey('ProductType', on_delete=models.SET_NULL, null=True)
97 173
     description = models.TextField(blank=True, null=True)
98 174
     store = models.ForeignKey('Store', on_delete=models.CASCADE, null=True, blank=False)
99
-    price = models.DecimalField(null=True, blank=True, decimal_places=2, max_digits=7)
175
+    price = models.DecimalField(null=True, blank=True, decimal_places=2, max_digits=10)
176
+
177
+
178
+    details  = models.JSONField(null=True, blank=True)
179
+
180
+    n_unit = models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True)
181
+    unit_name = models.CharField(max_length=200, null=True)
182
+
183
+    def __str__(self):
184
+        return f"{self.name} {self.code}"
185
+
186
+class Vendor(GenericModel, models.Model ):
187
+    name = models.CharField(max_length=200)
188
+    code = models.CharField(max_length=200)
189
+    #product_type = TreeForeignKey('ProductType', on_delete=models.SET_NULL, null=True)
190
+    product = models.ForeignKey('Product', on_delete=models.CASCADE)
191
+    description = models.TextField(blank=True, null=True)
192
+    store = models.ForeignKey('Store', on_delete=models.CASCADE, null=True, blank=False)
193
+    price = models.DecimalField(null=True, blank=True, decimal_places=2, max_digits=10)
194
+
100 195
 
101 196
     details  = models.JSONField(null=True, blank=True)
102 197
 
103 198
     n_unit = models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True)
104 199
     unit_name = models.CharField(max_length=200, null=True)
105 200
 
201
+    tel = models.CharField(max_length=100, null=True, blank=True)
202
+    line_id = models.CharField(max_length=100, null=True, blank=True)
203
+    email = models.EmailField(null=True, blank=True)
204
+
205
+    address_text = models.TextField(blank=False, null=False)
206
+    address = map_fields.AddressField(max_length=200)
207
+    geolocation = map_fields.GeoLocationField(max_length=100)
208
+
209
+
106 210
     def __str__(self):
107 211
         return f"{self.name} {self.code}"
108 212
 
213
+
109 214
 class ProductSKU(GenericModel, models.Model ):
110 215
     sku = models.CharField(max_length=200)
111 216
     product  = models.ForeignKey('Product', on_delete=models.CASCADE, null=True)
@@ -117,8 +222,12 @@ class ProductSKU(GenericModel, models.Model ):
117 222
     n_unit = models.DecimalField(blank=True, decimal_places=2, max_digits=7, null=True)
118 223
     unit_name = models.CharField(max_length=200, null=True)
119 224
 
225
+    def __str__(self):
226
+        return f"{self.sku} {self.product} @{self.price}(THB)"
227
+
120 228
 class Photo(GenericModel, models.Model):
121 229
     name = models.CharField(max_length=200,  blank=True)
230
+    order_n = models.IntegerField(default=0, blank=True)
122 231
     photo  = models.ImageField(upload_to="uploads/%Y/%m/%d/", blank=False, verbose_name="Photo")
123 232
     product = models.ForeignKey('Product', on_delete=models.CASCADE, null=True)
124 233
 

+ 17 - 0
app/fruit/templates/fruit/_paging.html

@@ -0,0 +1,17 @@
1
+<div class="pagination">
2
+    <span class="step-links">
3
+        {% if page_obj.has_previous %}
4
+            <a href="?page=1">&laquo; first</a>
5
+            <a href="?page={{ page_obj.previous_page_number }}">previous</a>
6
+        {% endif %}
7
+
8
+        <span class="current">
9
+            Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
10
+        </span>
11
+
12
+        {% if page_obj.has_next %}
13
+            <a href="?page={{ page_obj.next_page_number }}">next</a>
14
+            <a href="?page={{ page_obj.paginator.num_pages }}">last &raquo;</a>
15
+        {% endif %}
16
+    </span>
17
+</div>

+ 13 - 0
app/fruit/templates/fruit/_searchcenter.html

@@ -0,0 +1,13 @@
1
+{% load crispy_forms_tags %}
2
+<form method="get">
3
+    <div class='row'>
4
+        <!--
5
+            {{ filter.form.fields }} -->
6
+    {% for f0  in filter.form %}
7
+        <div class='col-md-3'>
8
+            {{ f0 | as_crispy_field}}  
9
+        </div>
10
+    {% endfor %}
11
+    </div>
12
+    <input type="submit" class='btn btn-primary' value='Search'/>
13
+</form>

+ 25 - 0
app/fruit/templates/fruit/inbox_form.html

@@ -0,0 +1,25 @@
1
+{% extends "fruit/inbox_index.html" %}
2
+{% load static %}
3
+{% load crispy_forms_tags %}
4
+{% load django_bootstrap_breadcrumbs %}
5
+
6
+{% block header %}            
7
+{{ form.media  }}
8
+{% endblock %}
9
+
10
+{% block breadcrumbs %}
11
+    {{ block.super }}
12
+    {% breadcrumb "Inbox Form" "fruit:inbox_edit" form.instance.pk %}
13
+{% endblock %}
14
+
15
+{% block store_main %}
16
+<h2>Inbox Form</h2>
17
+<form  method="post" enctype="multipart/form-data">
18
+    {% csrf_token %}
19
+    {{ form | crispy  }}
20
+    <br>
21
+    <input type='submit' class='btn btn-primary' value="Update" />
22
+
23
+</form>
24
+
25
+{% endblock %}

+ 50 - 0
app/fruit/templates/fruit/inbox_index.html

@@ -0,0 +1,50 @@
1
+{% extends "fruit/mystore.html" %}
2
+{% load static %}
3
+{% load crispy_forms_tags %}
4
+
5
+{% block header %}            
6
+{% endblock %}
7
+
8
+{% load django_bootstrap_breadcrumbs %}
9
+{% block breadcrumbs %}
10
+    {{ block.super }}
11
+    {% breadcrumb "Inboxes" "fruit:inbox_index" %}
12
+{% endblock %}
13
+
14
+{% block store_main %}
15
+<!--
16
+<a  class='btn btn-primary' href="{% url "fruit:product_create" %}">Create Product</a> -->
17
+<hr>
18
+<h2>
19
+    Inbox Index</h2>
20
+{% include "fruit/_searchcenter.html" %}
21
+<hr>
22
+<table class='table table-borded table-striped'>
23
+    <thead>
24
+        <tr>
25
+            <th>ID</th>
26
+            <th>Store</th>
27
+            <th>Product</th>
28
+            <th>Buyer</th>
29
+            <th>Subject</th>
30
+            <th>Body</th>
31
+            <th>Status</th>
32
+            <th>Created At</th></tr>
33
+    </thead>
34
+    <tbody>
35
+{% for p in page_obj %}
36
+        <tr>
37
+            <td><a href="{% url "fruit:inbox_edit" pk=p.pk %}">{{ p.id }}</a></td><td>{{ p.product }}</td>
38
+            <td>{{ p.store}}</td>
39
+            <td>{{ p.product }}</td>
40
+            <td>{{ p.buyer }}</td>
41
+            <td>{{ p.subject }}</td>
42
+            <td>{{ p.body }}</td>
43
+            <td>{{ p.status }}</td>
44
+            <td>{{ p.created_at }}</td>
45
+        </tr>
46
+{% endfor %}
47
+    </tbody>
48
+</table>
49
+{% include "fruit/_paging.html" %}
50
+{% endblock %}

+ 12 - 3
app/fruit/templates/fruit/mystore.html

@@ -1,6 +1,7 @@
1 1
 {% extends "front/base.html" %}
2 2
 {% load static %}
3 3
 {% load crispy_forms_tags %}
4
+{% load django_bootstrap_breadcrumbs %}
4 5
 
5 6
 {% block header %}            
6 7
 {{ storeForm.media  }}
@@ -8,21 +9,29 @@
8 9
 
9 10
 {% block content %}
10 11
 <h1>My Store</h1>
12
+<!-- 
11 13
 <form method=post>
12 14
     {% csrf_token %}
13 15
     <input type='text' name='name' class='form-control' placeholder='Store Name'/>
14 16
     <input type='submit' class='btn btn-primary' name='createStore' value="Create Store" />
15 17
 </form>
16
-<hr>
18
+<hr> -->
17 19
 <div class="d-flex align-items-start">
18 20
   <div class="nav flex-column nav-pills me-3 col-md-3 col-lg-2" id="v-pills-tab" role="tablist" aria-orientation="vertical">
19 21
       <a class="nav-link {{ mystore|yesno:"active," }}" id="v-pills-home-tab"  href="{% url "fruit:mystore" %}" type="button" role="tab" aria-controls="v-pills-home" aria-selected="true">Info</a>
20 22
       <a class="nav-link {{ product|yesno:"active," }}" id="v-pills-profile-tab"  href="{% url "fruit:product_index" %}" type="button" role="tab" aria-controls="v-pills-profile" aria-selected="false">Products</a>
21
-    <a class="nav-link" id="v-pills-messages-tab" data-toggle="pill" data-bs-target="#v-pills-messages" type="button" role="tab" aria-controls="v-pills-messages" aria-selected="false">Inbox</a>
22
-    <a class="nav-link" id="v-pills-settings-tab" data-toggle="pill" data-bs-target="#v-pills-settings" type="button" role="tab" aria-controls="v-pills-settings" aria-selected="false">Saled Items</a>
23
+    <a class="nav-link" id="v-pills-messages-tab" href="{% url "fruit:inbox_index" %}" type="button" role="tab" aria-controls="v-pills-messages" aria-selected="false">Inbox</a>
24
+    <a class="nav-link {{ sale_active|yesno:"active," }}" id="v-pills-settings-tab" href="{% url "fruit:sale_index" %}"  type="button" role="tab" aria-controls="v-pills-settings" aria-selected="false">Saled Items</a>
23 25
     <a class="nav-link" id="v-pills-vendor-tab" data-toggle="pill" data-bs-target="#v-pills-vendor" type="button" role="tab" aria-controls="v-pills-vendor" aria-selected="false">Vendors</a>
24 26
   </div>
25 27
   <div class="tab-content p-3 col-md-9 col-lg-10" id="v-pills-tabContent">
28
+      {% block breadcrumbs %}
29
+          {% clear_breadcrumbs %}
30
+          {% breadcrumb "My Store" "fruit:mystore" %}
31
+      {% endblock %}
32
+
33
+
34
+    {% render_breadcrumbs %} 
26 35
       {% block store_main %}
27 36
       {% endblock %}
28 37
   </div>

+ 36 - 30
app/fruit/templates/fruit/product_form.html

@@ -1,4 +1,4 @@
1
-{% extends "fruit/mystore.html" %}
1
+{% extends "fruit/product_index.html" %}
2 2
 {% load static %}
3 3
 {% load crispy_forms_tags %}
4 4
 
@@ -7,41 +7,47 @@
7 7
 {{ form2.media  }}
8 8
 {% endblock %}
9 9
 
10
+{% load django_bootstrap_breadcrumbs %}
11
+{% block breadcrumbs %}
12
+    {{ block.super }}
13
+    {% breadcrumb "Product Edit" "fruit:product_edit" pid  %}
14
+{% endblock %}
15
+
10 16
 {% block store_main %}
11 17
     Create Product
12 18
 
19
+
13 20
 <form  method="post" enctype="multipart/form-data">
14 21
     {% csrf_token %}
15 22
     {{ form | crispy  }}
16
-    <fieldset class="border p-2">
17
-        <legend class='w-auto d-inline-block form-legend p-3'>Photos</legend>
18
-        {{ form2.management_form }}
19
-        <div class='row'>
20
-        {% for f0  in form2 %}
21
-        <div class='col-md-3'>
22
-        <!-- {{ f0.fields }} -->
23
-
24
-            {{ f0.id  | as_crispy_field }}
25
-            {{ f0.name | as_crispy_field }}
26
-            {% if f0.instance.photo %}
27
-            <a href="{{ f0.instance.photo.url }}" target="_blank">
28
-                <img src="{{ f0.instance.photo.url  }}" width='100%' style='max-height:200px'></a>
29
-            {% endif %}
30
-            {{ f0.photo | as_crispy_field }}
31
-            
32
-            {{ f0.product | as_crispy_field }}
33
-            {{ f0.DELETE | as_crispy_field }}
34
-        </div>
35
-        {% endfor %}
36
-        </div>
37
-    </fieldset>
38
-    <br>
39
-    <input type='submit' class='btn btn-primary' name='updateStore' value="Update" />
23
+        <fieldset class="border p-2 row">
24
+            <legend class='w-auto d-inline-block form-legend p-3'>Photos</legend>
25
+            {{ form2.management_form }}
26
+            {% for f0  in form2 %}
27
+            <div class='col-md-3 border'>
28
+            <!-- {{ f0.fields }} -->
29
+
30
+                {{ f0.id  | as_crispy_field }}
31
+                {{ f0.name | as_crispy_field }}
32
+                {% if f0.instance.photo %}
33
+                <a href="{{ f0.instance.photo.url }}" target="_blank">
34
+                    <img src="{{ f0.instance.photo.url  }}" width='100%' style='max-height:200px'></a>
35
+                {% endif %}
36
+                {{ f0.photo | as_crispy_field }}
37
+                
38
+                {{ f0.order_n  | as_crispy_field }}
39
+                {{ f0.product | as_crispy_field }}
40
+                {{ f0.DELETE | as_crispy_field }}
41
+            </div>
42
+            {% endfor %}
43
+        </fieldset>
44
+        <br>
45
+        <input type='submit' class='btn btn-primary' name='updateStore' value="Update" />
40 46
 
41
-</form>
42
-<hr>
43
-<h2>SKUs</h2>
44
-<a href="{% url "fruit:create_sku" pk=object.pk %}" class='btn btn-primary'>Create SKU</a><br><br>
47
+    </form>
48
+    <hr>
49
+    <h2>SKUs</h2>
50
+    <a href="{% url "fruit:create_sku" pk=obj.pk %}" class='btn btn-primary'>Create SKU</a><br><br>
45 51
 
46 52
 <table class='table table-bordered  table-striped'>
47 53
     <thead>
@@ -50,7 +56,7 @@
50 56
     </thead>
51 57
     <tbody>
52 58
         
53
-        {% for o in  object.productsku_set.all %}
59
+        {% for o in  obj.productsku_set.all %}
54 60
             <tr>
55 61
                 <td><a href="{% url "fruit:edit_sku" pk=o.pk %}">{{ o.sku }}</a></td>
56 62
                 <td>{{ o.created_at }}</td>

+ 17 - 6
app/fruit/templates/fruit/product_index.html

@@ -5,21 +5,32 @@
5 5
 {% block header %}            
6 6
 {% endblock %}
7 7
 
8
+{% load django_bootstrap_breadcrumbs %}
9
+{% block breadcrumbs %}
10
+    {{ block.super }}
11
+    {% breadcrumb "Products" "fruit:product_index" %}
12
+{% endblock %}
13
+
8 14
 {% block store_main %}
9
-<a  href="{% url "fruit:product_create" %}">Create Product</a>
15
+<a  class='btn btn-primary' href="{% url "fruit:product_create" %}">Create Product</a>
16
+<hr>
17
+<h2>
18
+    Product Index</h2>
19
+{% include "fruit/_searchcenter.html" %}
10 20
 <hr>
11
-Product Index
12
-{{ products }}
21
+<!--
22
+    {{ products }} -->
13 23
 <table class='table table-borded table-striped'>
14 24
     <thead>
15
-        <tr><th>ID</th><th>Name</th><th>Created At</th></tr>
25
+        <tr><th>ID</th><th>Name</th><th>Price</th><th>Created At</th></tr>
16 26
     </thead>
17 27
     <tbody>
18
-{% for p in products %}
28
+{% for p in page_obj %}
19 29
         <tr>
20
-            <td><a href="{% url "fruit:product_edit" pk=p.pk %}">{{ p.id }}</a></td><td>{{ p.name }}</td><td>{{ p.created_at }}</td>
30
+            <td><a href="{% url "fruit:product_edit" pk=p.pk %}">{{ p.id }}</a></td><td>{{ p.name }}</td><td>{% firstof p.price  "-" %}</td><td>{{ p.created_at }}</td>
21 31
         </tr>
22 32
 {% endfor %}
23 33
     </tbody>
24 34
 </table>
35
+{% include "fruit/_paging.html" %}
25 36
 {% endblock %}

+ 25 - 0
app/fruit/templates/fruit/sale_form.html

@@ -0,0 +1,25 @@
1
+{% extends "fruit/sale_index.html" %}
2
+{% load static %}
3
+{% load crispy_forms_tags %}
4
+{% load django_bootstrap_breadcrumbs %}
5
+
6
+{% block header %}            
7
+{{ form.media  }}
8
+{% endblock %}
9
+
10
+{% block breadcrumbs %}
11
+    {{ block.super }}
12
+    {% breadcrumb "Sales Form" "fruit:sale_edit" form.instance.pk %}
13
+{% endblock %}
14
+
15
+{% block store_main %}
16
+<h2>Sale Form</h2>
17
+<form  method="post" enctype="multipart/form-data">
18
+    {% csrf_token %}
19
+    {{ form | crispy  }}
20
+    <br>
21
+    <input type='submit' class='btn btn-primary' value="Update" />
22
+
23
+</form>
24
+
25
+{% endblock %}

+ 41 - 0
app/fruit/templates/fruit/sale_index.html

@@ -0,0 +1,41 @@
1
+{% extends "fruit/mystore.html" %}
2
+{% load static %}
3
+{% load crispy_forms_tags %}
4
+
5
+{% block header %}            
6
+{% endblock %}
7
+
8
+{% load django_bootstrap_breadcrumbs %}
9
+{% block breadcrumbs %}
10
+    {{ block.super }}
11
+    {% breadcrumb "Sales" "fruit:sale_index" %}
12
+{% endblock %}
13
+
14
+{% block store_main %}
15
+<!--
16
+<a  class='btn btn-primary' href="{% url "fruit:product_create" %}">Create Product</a> -->
17
+<hr>
18
+<h2>
19
+    Sale Index</h2>
20
+{% include "fruit/_searchcenter.html" %}
21
+<hr>
22
+<table class='table table-borded table-striped'>
23
+    <thead>
24
+        <tr><th>ID</th><th>Product</th><th>SKU</th><th>Price</th><th>Unit(s)</th><th>UName</th><th>Total</th><th>Buyer</th><th>Created At</th></tr>
25
+    </thead>
26
+    <tbody>
27
+{% for p in page_obj %}
28
+        <tr>
29
+            <td><a href="{% url "fruit:sale_edit" pk=p.pk %}">{{ p.id }}</a></td><td>{{ p.product }}</td>
30
+            <td>{{ p.sku }}</td>
31
+            <td>{{ p.n_unit }}</td>
32
+            <td>{{ p.unit_name }}</td>
33
+            <td>{{ p.total }}</td>
34
+            <td>{{ p.buyer }}</td>
35
+            <td>{% firstof p.price  "-" %}</td><td>{{ p.created_at }}</td>
36
+        </tr>
37
+{% endfor %}
38
+    </tbody>
39
+</table>
40
+{% include "fruit/_paging.html" %}
41
+{% endblock %}

+ 7 - 1
app/fruit/templates/fruit/sku_form.html

@@ -1,4 +1,4 @@
1
-{% extends "fruit/mystore.html" %}
1
+{% extends "fruit/product_form.html" %}
2 2
 {% load static %}
3 3
 {% load crispy_forms_tags %}
4 4
 
@@ -6,6 +6,12 @@
6 6
 {{ form.media  }}
7 7
 {% endblock %}
8 8
 
9
+{% load django_bootstrap_breadcrumbs %}
10
+{% block breadcrumbs %}
11
+    {{ block.super }}
12
+    {% breadcrumb "SKU Edit" "fruit:sku_edit" pk=form.instance.pk  %}
13
+{% endblock %}
14
+
9 15
 {% block store_main %}
10 16
     SKU Form
11 17
 

+ 4 - 0
app/fruit/urls.py

@@ -11,6 +11,10 @@ urlpatterns = [
11 11
     path('sku/<pk>', views.edit_sku, name='edit_sku'),
12 12
     path('products/', views.product_index, name='product_index'),
13 13
     path('products/<pk>', views.product_edit, name='product_edit'),
14
+    path('sales/', views.sale_index, name='sale_index'),
15
+    path('sales/<pk>', views.sale_edit, name='sale_edit'),
16
+    path('inbox/', views.inbox_index, name='inbox_index'),
17
+    path('inbox/<pk>', views.inbox_edit, name='inbox_edit'),
14 18
     path('signup', views.signup, name='signup'),
15 19
 ]
16 20
 

+ 98 - 6
app/fruit/views.py

@@ -6,9 +6,10 @@ from django.contrib.auth.forms import UserCreationForm
6 6
 from django.urls import reverse
7 7
 from django.contrib.auth.decorators import login_required
8 8
 
9
-from fruit.models import Store, Product, Photo, ProductSKU
10
-from .forms import StoreForm, ProductForm, PhotoFormSet, InlinePhotoFormset, ProductSKUForm
9
+from fruit.models import Store, Product, Photo, ProductSKU, Sale, Inbox
10
+from .forms import StoreForm, ProductForm, InboxForm, SaleForm,  PhotoFormSet, InlinePhotoFormset, ProductSKUForm, ProductFilter, SaleFilter, InboxFilter
11 11
 from django.contrib import messages
12
+from django.core.paginator import Paginator
12 13
 
13 14
 
14 15
 def index(request):
@@ -48,7 +49,27 @@ def mystore(request):
48 49
 def product_index(request):
49 50
     stores = request.user.store_created.all().order_by("-created_at")
50 51
     products = stores[0].product_set.all().order_by("-created_at")
51
-    return render(request, 'fruit/product_index.html', {'products': products, 'product': True})
52
+
53
+    f = ProductFilter(request.GET, queryset=products)
54
+
55
+    paginator = Paginator(f.qs, 25)
56
+    page_number = request.GET.get('page')
57
+    page_obj = paginator.get_page(page_number)
58
+
59
+    return render(request, 'fruit/product_index.html', {'products': products, 'product': True, 'page_obj': page_obj, 'filter': f})
60
+
61
+@login_required
62
+def sale_index(request):
63
+    stores = request.user.store_created.all().order_by("-created_at")
64
+    o_qs = stores[0].sale_set.all().order_by("-created_at")
65
+
66
+    f = SaleFilter(request.GET, queryset=o_qs)
67
+
68
+    paginator = Paginator(f.qs, 25)
69
+    page_number = request.GET.get('page')
70
+    page_obj = paginator.get_page(page_number)
71
+
72
+    return render(request, 'fruit/sale_index.html', {'o_qs': o_qs, 'sale_active': True, 'page_obj': page_obj, 'filter': f})
52 73
 
53 74
 @login_required
54 75
 def create_product(request):
@@ -97,11 +118,14 @@ def create_sku(request, pk):
97 118
             message.error(request, "SKU  created failed")
98 119
             return redirect("fruit:create_sku", pk=int(pk))
99 120
 
100
-    return render(request, 'fruit/sku_form.html', {'form': form})
121
+    return render(request, 'fruit/sku_form.html', {'form': form, 'pid': p.pk})
101 122
 
102 123
 @login_required
103 124
 def edit_sku(request, pk):
125
+
104 126
     p = ProductSKU.objects.get(pk=pk)
127
+    p0 = p.product
128
+
105 129
     form = ProductSKUForm(instance=p)
106 130
     if request.method == "POST":
107 131
         form = ProductSKUForm(request.POST)
@@ -113,7 +137,7 @@ def edit_sku(request, pk):
113 137
             message.error(request, "SKU  created failed")
114 138
             return redirect("fruit:create_sku", pk=int(pk))
115 139
 
116
-    return render(request, 'fruit/sku_form.html', {'form': form})
140
+    return render(request, 'fruit/sku_form.html', {'form': form, 'pid': p0.pk})
117 141
 
118 142
 @login_required
119 143
 def product_edit(request, pk):
@@ -151,7 +175,75 @@ def product_edit(request, pk):
151 175
 
152 176
         return redirect("fruit:product_edit", pk =  int(pk))
153 177
 
154
-    return render(request, 'fruit/product_form.html', {'product': True, 'form': form, 'form2': form2, 'object': product })
178
+    return render(request, 'fruit/product_form.html', {'product': True, 'form': form, 'form2': form2, 'obj': product })
179
+
180
+@login_required
181
+def sale_edit(request, pk):
182
+    stores = request.user.store_created.all().order_by("-created_at")
183
+
184
+    obj = Sale.objects.get(pk=pk)
185
+    form = SaleForm(instance = obj)
186
+
187
+    if request.method == "POST":
188
+        form = SaleForm(request.POST)
189
+        if form.is_valid():
190
+            instance1 = form.save()
191
+            '''
192
+            print(instances)
193
+            for s in instances:
194
+                s.product = instance1
195
+                s.save()
196
+            '''
197
+            messages.success(request, "Sale Save")
198
+        else:
199
+            print("Invalid ")
200
+            if form.errors:
201
+                messages.error(request, form.errors)
202
+
203
+        return redirect("fruit:sale_edit", pk =  int(pk))
204
+
205
+    return render(request, 'fruit/sale_form.html', {'sale_active': True, 'form': form, 'object': obj})
206
+
207
+
208
+@login_required
209
+def inbox_index(request):
210
+    stores = request.user.store_created.all().order_by("-created_at")
211
+    o_qs = stores[0].inbox_set.all().order_by("-created_at")
212
+
213
+    f = InboxFilter(request.GET, queryset=o_qs)
214
+
215
+    paginator = Paginator(f.qs, 25)
216
+    page_number = request.GET.get('page')
217
+    page_obj = paginator.get_page(page_number)
218
+
219
+    return render(request, 'fruit/inbox_index.html', {'o_qs': o_qs, 'inbox_active': True, 'page_obj': page_obj, 'filter': f})
220
+
221
+@login_required
222
+def inbox_edit(request, pk):
223
+    stores = request.user.store_created.all().order_by("-created_at")
224
+
225
+    obj = Inbox.objects.get(pk=pk)
226
+    form = InboxForm(instance = obj)
227
+
228
+    if request.method == "POST":
229
+        form = InboxForm(request.POST)
230
+        if form.is_valid():
231
+            instance1 = form.save()
232
+            '''
233
+            print(instances)
234
+            for s in instances:
235
+                s.product = instance1
236
+                s.save()
237
+            '''
238
+            messages.success(request, "Sale Save")
239
+        else:
240
+            print("Invalid ")
241
+            if form.errors:
242
+                messages.error(request, form.errors)
243
+
244
+        return redirect("fruit:sale_edit", pk =  int(pk))
245
+
246
+    return render(request, 'fruit/inbox_form.html', {'inbox_active': True, 'form': form, 'object': obj})
155 247
 
156 248
 def signup(request):
157 249
     if request.method == 'POST':

+ 3 - 0
app/requirements.txt

@@ -14,3 +14,6 @@ Pillow
14 14
 django-quill-editor
15 15
 django-taggit
16 16
 django-crispy-forms
17
+django-filter
18
+django-paypal
19
+django-bootstrap-breadcrumbs

BIN
app/shaqfindbed/__pycache__/settings.cpython-39.pyc


+ 3 - 0
app/shaqfindbed/settings.py

@@ -47,12 +47,14 @@ INSTALLED_APPS = [
47 47
     'dal_select2',
48 48
     'mptt',
49 49
     'django_google_maps',
50
+    'django_bootstrap_breadcrumbs',
50 51
     'django.contrib.admin',
51 52
     'django.contrib.auth',
52 53
     'django.contrib.contenttypes',
53 54
     'django.contrib.sessions',
54 55
     'django.contrib.messages',
55 56
     'import_export',
57
+    'django_filters',
56 58
     'django_quill',
57 59
     'crispy_forms',
58 60
     'taggit',
@@ -171,3 +173,4 @@ LOGIN_REDIRECT_URL = '/fruit/mystore'
171 173
 LOGIN_URL = '/login'
172 174
 
173 175
 CRISPY_TEMPLATE_PACK = "bootstrap4"
176
+BREADCRUMBS_TEMPLATE = "django_bootstrap_breadcrumbs/bootstrap4.html"

tum/freight - Gogs: Simplico Git Service

Bez popisu

jquery.js 1.9KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. // Use the right jQuery source on the test page (and iframes)
  2. ( function() {
  3. /* global loadTests: false */
  4. var config, src,
  5. FILEPATH = "/test/jquery.js",
  6. activeScript = [].slice.call( document.getElementsByTagName( "script" ), -1 )[ 0 ],
  7. parentUrl = activeScript && activeScript.src ?
  8. activeScript.src.replace( /[?#].*/, "" ) + FILEPATH.replace( /[^/]+/g, ".." ) + "/" :
  9. "../",
  10. QUnit = window.QUnit,
  11. require = window.require;
  12. function getQUnitConfig() {
  13. var config = Object.create( null );
  14. // Default to unminified jQuery for directly-opened iframes
  15. if ( !QUnit ) {
  16. config.dev = true;
  17. } else {
  18. // QUnit.config is populated from QUnit.urlParams but only at the beginning
  19. // of the test run. We need to read both.
  20. QUnit.config.urlConfig.forEach( function( entry ) {
  21. config[ entry.id ] = QUnit.config[ entry.id ] != null ?
  22. QUnit.config[ entry.id ] :
  23. QUnit.urlParams[ entry.id ];
  24. } );
  25. }
  26. return config;
  27. }
  28. // Define configuration parameters controlling how jQuery is loaded
  29. if ( QUnit ) {
  30. QUnit.config.urlConfig.push( {
  31. id: "amd",
  32. label: "Load with AMD",
  33. tooltip: "Load the AMD jQuery file (and its dependencies)"
  34. }, {
  35. id: "dev",
  36. label: "Load unminified",
  37. tooltip: "Load the development (unminified) jQuery file"
  38. } );
  39. }
  40. config = getQUnitConfig();
  41. src = config.dev ?
  42. "dist/jquery.js" :
  43. "dist/jquery.min.js";
  44. // Honor AMD loading on the main window (detected by seeing QUnit on it).
  45. // This doesn't apply to iframes because they synchronously expect jQuery to be there.
  46. if ( config.amd && QUnit ) {
  47. require.config( {
  48. baseUrl: parentUrl
  49. } );
  50. src = "src/jquery";
  51. // Include tests if specified
  52. if ( typeof loadTests !== "undefined" ) {
  53. require( [ src ], loadTests );
  54. } else {
  55. require( [ src ] );
  56. }
  57. // Otherwise, load synchronously
  58. } else {
  59. document.write( "<script id='jquery-js' nonce='jquery+hardcoded+nonce' src='" + parentUrl + src + "'><\x2Fscript>" );
  60. }
  61. } )();