├── apps ├── __init__.py ├── base │ ├── __init__.py │ ├── migrations │ │ └── __init__.py │ ├── admin.py │ ├── tests.py │ ├── views.py │ ├── apps.py │ ├── api.py │ ├── utils.py │ └── models.py ├── users │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ ├── routers.py │ │ ├── serializers.py │ │ └── api.py │ ├── migrations │ │ ├── __init__.py │ │ └── 0001_initial.py │ ├── apps.py │ ├── admin.py │ ├── authentication_mixins.py │ ├── authentication.py │ ├── models.py │ └── views.py ├── products │ ├── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0005_auto_20220501_1737.py │ │ ├── 0004_auto_20220501_1729.py │ │ ├── 0002_auto_20211104_2341.py │ │ ├── 0003_historicalcategoryproduct_historicalindicator_historicalmeasureunit_historicalproduct.py │ │ └── 0001_initial.py │ ├── apps.py │ ├── admin.py │ ├── api │ │ ├── urls.py │ │ ├── routers.py │ │ ├── serializers │ │ │ ├── general_serializers.py │ │ │ └── product_serializers.py │ │ └── viewsets │ │ │ ├── product_viewsets.py │ │ │ └── general_views.py │ └── models.py └── expense_manager │ ├── __init__.py │ ├── migrations │ ├── __init__.py │ ├── 0003_auto_20220211_0708.py │ ├── 0004_auto_20220211_0716.py │ ├── 0002_auto_20220120_2142.py │ └── 0001_initial.py │ ├── tests.py │ ├── views.py │ ├── apps.py │ ├── admin.py │ ├── api │ ├── routers.py │ ├── serializers │ │ ├── expense_serializer.py │ │ └── general_serializer.py │ └── viewsets │ │ └── expense_viewsets.py │ └── models.py ├── tests ├── __init__.py ├── factories │ ├── __init__.py │ └── expense_manager │ │ ├── __init__.py │ │ └── expense_factories.py ├── tests_expense_manager │ ├── __init__.py │ └── test_expense_viewset.py └── test_setup.py ├── ecommerce_rest ├── __init__.py ├── settings │ ├── __init__.py │ ├── production.py │ ├── local.py │ └── base.py ├── db.sqlite3 ├── media │ └── products │ │ ├── VGEQnxc.png │ │ ├── WEzQ6JB.png │ │ ├── E_k9_J0WYAkLY41.jpeg │ │ ├── VGEQnxc_AZZmCi4.png │ │ ├── VGEQnxc_rJCFFHG.png │ │ ├── VGEQnxc_szNHivm.png │ │ ├── WEzQ6JB_8PWW9XS.png │ │ ├── WEzQ6JB_M1mTvcd.png │ │ ├── WEzQ6JB_NOSvGER.png │ │ ├── E_k9_J0WYAkLY41_t1dhQtH.jpeg │ │ ├── Captura_de_pantalla_de_2021-10-14_17-49-05.png │ │ ├── Captura_de_pantalla_de_2021-10-14_17-49-05_0x5dSk3.png │ │ ├── Captura_de_pantalla_de_2021-10-14_17-49-05_BPKluW1.png │ │ ├── Captura_de_pantalla_de_2021-10-14_17-49-05_LYMu8HM.png │ │ ├── Captura_de_pantalla_de_2021-10-14_17-49-05_TUoQ8Sa.png │ │ └── Captura_de_pantalla_de_2021-10-14_17-49-05_UwyER8W.png ├── asgi.py ├── wsgi.py └── urls.py ├── README.md ├── manage.py ├── requirements.txt ├── .gitignore └── da /apps/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/base/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/users/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/products/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/users/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ecommerce_rest/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/factories/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/base/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/expense_manager/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/users/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/products/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ecommerce_rest/settings/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/expense_manager/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/factories/expense_manager/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/tests_expense_manager/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/base/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /apps/base/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /apps/base/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /apps/expense_manager/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /apps/expense_manager/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /ecommerce_rest/db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/ecommerce_rest/HEAD/ecommerce_rest/db.sqlite3 -------------------------------------------------------------------------------- /apps/base/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class BaseConfig(AppConfig): 5 | name = 'base' 6 | -------------------------------------------------------------------------------- /apps/users/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class UsersConfig(AppConfig): 5 | name = 'users' 6 | -------------------------------------------------------------------------------- /apps/products/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ProductsConfig(AppConfig): 5 | name = 'products' 6 | -------------------------------------------------------------------------------- /apps/users/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from apps.users.models import User 3 | 4 | admin.site.register(User) 5 | -------------------------------------------------------------------------------- /ecommerce_rest/media/products/VGEQnxc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/ecommerce_rest/HEAD/ecommerce_rest/media/products/VGEQnxc.png -------------------------------------------------------------------------------- /ecommerce_rest/media/products/WEzQ6JB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/ecommerce_rest/HEAD/ecommerce_rest/media/products/WEzQ6JB.png -------------------------------------------------------------------------------- /apps/expense_manager/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ExpenseManagerConfig(AppConfig): 5 | name = 'expense_manager' 6 | -------------------------------------------------------------------------------- /ecommerce_rest/media/products/E_k9_J0WYAkLY41.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/ecommerce_rest/HEAD/ecommerce_rest/media/products/E_k9_J0WYAkLY41.jpeg -------------------------------------------------------------------------------- /ecommerce_rest/media/products/VGEQnxc_AZZmCi4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/ecommerce_rest/HEAD/ecommerce_rest/media/products/VGEQnxc_AZZmCi4.png -------------------------------------------------------------------------------- /ecommerce_rest/media/products/VGEQnxc_rJCFFHG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/ecommerce_rest/HEAD/ecommerce_rest/media/products/VGEQnxc_rJCFFHG.png -------------------------------------------------------------------------------- /ecommerce_rest/media/products/VGEQnxc_szNHivm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/ecommerce_rest/HEAD/ecommerce_rest/media/products/VGEQnxc_szNHivm.png -------------------------------------------------------------------------------- /ecommerce_rest/media/products/WEzQ6JB_8PWW9XS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/ecommerce_rest/HEAD/ecommerce_rest/media/products/WEzQ6JB_8PWW9XS.png -------------------------------------------------------------------------------- /ecommerce_rest/media/products/WEzQ6JB_M1mTvcd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/ecommerce_rest/HEAD/ecommerce_rest/media/products/WEzQ6JB_M1mTvcd.png -------------------------------------------------------------------------------- /ecommerce_rest/media/products/WEzQ6JB_NOSvGER.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/ecommerce_rest/HEAD/ecommerce_rest/media/products/WEzQ6JB_NOSvGER.png -------------------------------------------------------------------------------- /ecommerce_rest/media/products/E_k9_J0WYAkLY41_t1dhQtH.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/ecommerce_rest/HEAD/ecommerce_rest/media/products/E_k9_J0WYAkLY41_t1dhQtH.jpeg -------------------------------------------------------------------------------- /ecommerce_rest/media/products/Captura_de_pantalla_de_2021-10-14_17-49-05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/ecommerce_rest/HEAD/ecommerce_rest/media/products/Captura_de_pantalla_de_2021-10-14_17-49-05.png -------------------------------------------------------------------------------- /ecommerce_rest/media/products/Captura_de_pantalla_de_2021-10-14_17-49-05_0x5dSk3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/ecommerce_rest/HEAD/ecommerce_rest/media/products/Captura_de_pantalla_de_2021-10-14_17-49-05_0x5dSk3.png -------------------------------------------------------------------------------- /ecommerce_rest/media/products/Captura_de_pantalla_de_2021-10-14_17-49-05_BPKluW1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/ecommerce_rest/HEAD/ecommerce_rest/media/products/Captura_de_pantalla_de_2021-10-14_17-49-05_BPKluW1.png -------------------------------------------------------------------------------- /ecommerce_rest/media/products/Captura_de_pantalla_de_2021-10-14_17-49-05_LYMu8HM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/ecommerce_rest/HEAD/ecommerce_rest/media/products/Captura_de_pantalla_de_2021-10-14_17-49-05_LYMu8HM.png -------------------------------------------------------------------------------- /ecommerce_rest/media/products/Captura_de_pantalla_de_2021-10-14_17-49-05_TUoQ8Sa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/ecommerce_rest/HEAD/ecommerce_rest/media/products/Captura_de_pantalla_de_2021-10-14_17-49-05_TUoQ8Sa.png -------------------------------------------------------------------------------- /ecommerce_rest/media/products/Captura_de_pantalla_de_2021-10-14_17-49-05_UwyER8W.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpe/ecommerce_rest/HEAD/ecommerce_rest/media/products/Captura_de_pantalla_de_2021-10-14_17-49-05_UwyER8W.png -------------------------------------------------------------------------------- /apps/users/api/routers.py: -------------------------------------------------------------------------------- 1 | from rest_framework.routers import DefaultRouter 2 | 3 | from apps.users.api.api import UserViewSet 4 | 5 | router = DefaultRouter() 6 | 7 | router.register('', UserViewSet, basename="users") 8 | 9 | urlpatterns = router.urls -------------------------------------------------------------------------------- /apps/base/api.py: -------------------------------------------------------------------------------- 1 | from rest_framework import generics 2 | 3 | class GeneralListApiView(generics.ListAPIView): 4 | serializer_class = None 5 | 6 | def get_queryset(self): 7 | model = self.get_serializer().Meta.model 8 | return model.objects.filter(state = True) 9 | -------------------------------------------------------------------------------- /apps/expense_manager/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | from apps.expense_manager.models import * 4 | 5 | admin.site.register(Supplier) 6 | admin.site.register(PaymentType) 7 | admin.site.register(Voucher) 8 | admin.site.register(ExpenseCategory) 9 | admin.site.register(Expense) 10 | -------------------------------------------------------------------------------- /apps/expense_manager/api/routers.py: -------------------------------------------------------------------------------- 1 | from rest_framework.routers import DefaultRouter 2 | 3 | from apps.expense_manager.api.viewsets.expense_viewsets import ExpenseViewSet 4 | 5 | router = DefaultRouter() 6 | 7 | router.register(r'expense', ExpenseViewSet, basename='expense') 8 | 9 | urlpatterns = router.urls -------------------------------------------------------------------------------- /ecommerce_rest/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for ecommerce_rest project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ecommerce_rest.settings.local') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /ecommerce_rest/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for ecommerce_rest project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ecommerce_rest.settings.local') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /apps/products/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from apps.products.models import * 3 | 4 | # Register your models here. 5 | class MeasureUnitAdmin(admin.ModelAdmin): 6 | list_display = ('id','description') 7 | 8 | class CategoryProductAdmin(admin.ModelAdmin): 9 | list_display = ('id','description') 10 | 11 | admin.site.register(MeasureUnit,MeasureUnitAdmin) 12 | admin.site.register(CategoryProduct,CategoryProductAdmin) 13 | admin.site.register(Indicator) 14 | admin.site.register(Product) 15 | -------------------------------------------------------------------------------- /apps/products/api/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from apps.products.api.viewsets.general_views import MeasureUnitListAPIView,IndicatorListAPIView,CategoryProductListAPIView 4 | from apps.products.api.viewsets.product_views import ( 5 | ProductListCreateAPIView,ProductRetrieveUpdateDestroyAPIView 6 | ) 7 | 8 | urlpatterns = [ 9 | path('measure_unit/', MeasureUnitListAPIView.as_view(), name = 'measure_unit'), 10 | path('indicator/', IndicatorListAPIView.as_view(), name = 'indicator'), 11 | ] -------------------------------------------------------------------------------- /ecommerce_rest/settings/production.py: -------------------------------------------------------------------------------- 1 | from .base import * 2 | 3 | # SECURITY WARNING: don't run with debug turned on in production! 4 | DEBUG = True 5 | 6 | ALLOWED_HOSTS = [] 7 | 8 | # Database 9 | # https://docs.djangoproject.com/en/3.1/ref/settings/#databases 10 | 11 | DATABASES = { 12 | 'default': { 13 | 'ENGINE': 'django.db.backends.sqlite3', 14 | 'NAME': BASE_DIR / 'db.sqlite3', 15 | } 16 | } 17 | 18 | 19 | # Static files (CSS, JavaScript, Images) 20 | # https://docs.djangoproject.com/en/3.1/howto/static-files/ 21 | 22 | STATIC_URL = '/static/' -------------------------------------------------------------------------------- /apps/products/migrations/0005_auto_20220501_1737.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.7 on 2022-05-01 17:37 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('products', '0004_auto_20220501_1729'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='historicalproduct', 15 | name='stock', 16 | ), 17 | migrations.RemoveField( 18 | model_name='product', 19 | name='stock', 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /apps/products/api/routers.py: -------------------------------------------------------------------------------- 1 | from rest_framework.routers import DefaultRouter 2 | from apps.products.api.viewsets.general_views import * 3 | from apps.products.api.viewsets.product_viewsets import ProductViewSet 4 | 5 | router = DefaultRouter() 6 | 7 | router.register(r'products',ProductViewSet,basename = 'products') 8 | router.register(r'measure-unit',MeasureUnitViewSet,basename = 'measure_unit') 9 | router.register(r'indicators',IndicatorViewSet,basename = 'indicators') 10 | router.register(r'category-products',CategoryProductViewSet,basename = 'category_products') 11 | 12 | urlpatterns = router.urls -------------------------------------------------------------------------------- /tests/factories/expense_manager/expense_factories.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | 3 | from apps.expense_manager.models import Supplier 4 | 5 | faker = Faker() 6 | 7 | class SupplierFactory: 8 | 9 | def build_supplier_JSON(self): 10 | return { 11 | 'ruc': str(faker.random_number(digits=11)), 12 | 'business_name': faker.company(), 13 | 'address': faker.address(), 14 | 'phone': str(faker.random_number(digits=11)), 15 | 'email': faker.email() 16 | } 17 | 18 | def create_supplier(self): 19 | return Supplier.objects.create(**self.build_supplier_JSON()) -------------------------------------------------------------------------------- /apps/base/utils.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | def validate_files(request, field, update=False): 4 | """ 5 | :params 6 | :request: request.data 7 | :field: key of file 8 | """ 9 | 10 | request = request.copy() 11 | 12 | if update: 13 | if type(request[field]) == str: request.__delitem__(field) 14 | else: 15 | if type(request[field]) == str: request.__setitem__(field, None) 16 | 17 | return request 18 | 19 | def format_date(date): 20 | date = datetime.strptime(date, '%d/%m/%Y') 21 | date = f"{date.year}-{date.month}-{date.day}" 22 | return date 23 | -------------------------------------------------------------------------------- /apps/expense_manager/migrations/0003_auto_20220211_0708.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.7 on 2022-02-11 07:08 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('expense_manager', '0002_auto_20220120_2142'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RenameField( 14 | model_name='expense', 15 | old_name='provider', 16 | new_name='supplier', 17 | ), 18 | migrations.RenameField( 19 | model_name='historicalexpense', 20 | old_name='provider', 21 | new_name='supplier', 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /apps/expense_manager/api/serializers/expense_serializer.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | from apps.expense_manager.models import Expense, Supplier 4 | 5 | class SupplierRegisterSerializer(serializers.ModelSerializer): 6 | 7 | class Meta: 8 | model = Supplier 9 | exclude = ('state','created_date','modified_date','deleted_date') 10 | 11 | def save(self): 12 | new_supplier = Supplier.objects.create(**self.validated_data) 13 | return new_supplier.to_dict() 14 | 15 | class ExpenseSerializer(serializers.ModelSerializer): 16 | 17 | class Meta: 18 | model = Expense 19 | exclude = ('state','created_date','modified_date','deleted_date') 20 | 21 | -------------------------------------------------------------------------------- /ecommerce_rest/settings/local.py: -------------------------------------------------------------------------------- 1 | import os 2 | from .base import * 3 | 4 | # SECURITY WARNING: don't run with debug turned on in production! 5 | DEBUG = True 6 | 7 | ALLOWED_HOSTS = [] 8 | 9 | # Database 10 | # https://docs.djangoproject.com/en/3.1/ref/settings/#databases 11 | 12 | DATABASES = { 13 | 'default': { 14 | 'ENGINE': 'django.db.backends.sqlite3', 15 | 'NAME': str(os.path.join(BASE_DIR, "db.sqlite3")), 16 | } 17 | } 18 | 19 | 20 | # Static files (CSS, JavaScript, Images) 21 | # https://docs.djangoproject.com/en/3.1/howto/static-files/ 22 | 23 | STATIC_URL = '/static/' 24 | STATICFILES_DIRS = (BASE_DIR, 'static') 25 | MEDIA_URL = '/media/' 26 | MEDIA_ROOT = os.path.join(BASE_DIR, 'media') 27 | -------------------------------------------------------------------------------- /apps/products/migrations/0004_auto_20220501_1729.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.7 on 2022-05-01 17:29 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('products', '0003_historicalcategoryproduct_historicalindicator_historicalmeasureunit_historicalproduct'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='historicalproduct', 15 | name='stock', 16 | field=models.IntegerField(default=0), 17 | ), 18 | migrations.AddField( 19 | model_name='product', 20 | name='stock', 21 | field=models.IntegerField(default=0), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This project belongs to Course: https://www.youtube.com/watch?v=MMFBD2Eoeuk&list=PLMbRqrU_kvbRI4PgSzgbh8XPEwC1RNj8F 2 | 3 | ### Initial Configuration 4 | 5 | This project include default migrations that you can see on the course, also include a SQLITE DB with example data. 6 | 7 | 1.- Clone this repository: 8 | 9 | git clone https://github.com/developerpe/ecommerce_rest.git 10 | 11 | 2.- Create a virtual environment : 12 | 13 | virtualenv name_env 14 | 15 | 3.- Active virtual environment. 16 | 17 | 4.- Install libraries. 18 | 19 | (env) pip install -r requirements.txt 20 | 21 | 5.- Run command: 22 | 23 | (env) python manage.py runserver 24 | 25 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ecommerce_rest.settings.local') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /apps/expense_manager/migrations/0004_auto_20220211_0716.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.7 on 2022-02-11 07:16 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('expense_manager', '0003_auto_20220211_0708'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='historicalsupplier', 15 | name='business_name', 16 | field=models.CharField(db_index=True, max_length=150, verbose_name='Razón Social'), 17 | ), 18 | migrations.AlterField( 19 | model_name='supplier', 20 | name='business_name', 21 | field=models.CharField(max_length=150, unique=True, verbose_name='Razón Social'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /apps/expense_manager/api/serializers/general_serializer.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | from apps.products.models import Product 4 | from apps.expense_manager.models import * 5 | 6 | class SupplierSerializer(serializers.ModelSerializer): 7 | 8 | class Meta: 9 | model = Supplier 10 | fields = ('id', 'ruc', 'business_name', 'address') 11 | 12 | class VoucherSerializer(serializers.ModelSerializer): 13 | 14 | class Meta: 15 | model = Voucher 16 | fields = ('id', 'name') 17 | 18 | class PaymentTypeSerializer(serializers.ModelSerializer): 19 | 20 | class Meta: 21 | model = PaymentType 22 | fields = ('id', 'name') 23 | 24 | class ProductSerializer(serializers.ModelSerializer): 25 | 26 | class Meta: 27 | model = Product 28 | fields = ('id', 'name') -------------------------------------------------------------------------------- /apps/expense_manager/migrations/0002_auto_20220120_2142.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.7 on 2022-01-20 21:42 2 | 3 | from django.conf import settings 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 11 | ('expense_manager', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RenameModel( 16 | old_name='HistoricalProvider', 17 | new_name='HistoricalSupplier', 18 | ), 19 | migrations.RenameModel( 20 | old_name='Provider', 21 | new_name='Supplier', 22 | ), 23 | migrations.RenameField( 24 | model_name='historicalmerma', 25 | old_name='money_loss', 26 | new_name='lost_money', 27 | ), 28 | migrations.RenameField( 29 | model_name='merma', 30 | old_name='money_loss', 31 | new_name='lost_money', 32 | ), 33 | ] 34 | -------------------------------------------------------------------------------- /tests/test_setup.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | 3 | from rest_framework import status 4 | from rest_framework.test import APITestCase 5 | 6 | class TestSetUp(APITestCase): 7 | 8 | def setUp(self): 9 | from apps.users.models import User 10 | 11 | faker = Faker() 12 | 13 | self.login_url = '/login/' 14 | self.user = User.objects.create_superuser( 15 | name='Developer', 16 | last_name='Developer', 17 | username=faker.name(), 18 | password='developer', 19 | email=faker.email() 20 | ) 21 | 22 | response = self.client.post( 23 | self.login_url, 24 | { 25 | 'username': self.user.username, 26 | 'password': 'developer' 27 | }, 28 | format='json' 29 | ) 30 | 31 | self.assertEqual(response.status_code, status.HTTP_200_OK) 32 | self.token = response.data['token'] 33 | self.client.credentials(HTTP_AUTHORIZATION='Bearer ' + self.token) 34 | return super().setUp() 35 | -------------------------------------------------------------------------------- /apps/base/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | from simple_history.models import HistoricalRecords 4 | 5 | # Create your models here. 6 | class BaseModel(models.Model): 7 | """Model definition for BaseModel.""" 8 | 9 | # TODO: Define fields here 10 | id = models.AutoField(primary_key = True) 11 | state = models.BooleanField('Estado',default = True) 12 | created_date = models.DateField('Fecha de Creación', auto_now=False, auto_now_add=True) 13 | modified_date = models.DateField('Fecha de Modificación', auto_now=True, auto_now_add=False) 14 | deleted_date = models.DateField('Fecha de Eliminación', auto_now=True, auto_now_add=False) 15 | historical = HistoricalRecords(user_model="users.User", inherit=True) 16 | 17 | @property 18 | def _history_user(self): 19 | return self.changed_by 20 | 21 | @_history_user.setter 22 | def _history_user(self, value): 23 | self.changed_by = value 24 | 25 | class Meta: 26 | """Meta definition for BaseModel.""" 27 | abstract = True 28 | verbose_name = 'Modelo Base' 29 | verbose_name_plural = 'Modelos Base' 30 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | appdirs==1.4.3 2 | asgiref==3.3.1 3 | CacheControl==0.12.6 4 | certifi==2019.11.28 5 | chardet==3.0.4 6 | colorama==0.4.3 7 | contextlib2==0.6.0 8 | coreapi==2.3.3 9 | coreschema==0.0.4 10 | distlib==0.3.0 11 | distro==1.4.0 12 | Django==3.1.7 13 | django-cors-headers==3.4.0 14 | django-rest-framework==0.1.0 15 | django-simple-history==2.11.0 16 | djangorestframework==3.11.0 17 | djangorestframework-simplejwt==5.0.0 18 | drf-yasg==1.20.0 19 | et-xmlfile==1.0.1 20 | Faker==13.3.4 21 | html5lib==1.0.1 22 | idna==2.8 23 | inflection==0.5.1 24 | ipaddr==2.2.0 25 | itypes==1.2.0 26 | Jinja2==2.11.3 27 | lockfile==0.12.2 28 | MarkupSafe==1.1.1 29 | msgpack==0.6.2 30 | openapi-codec==1.3.2 31 | openpyxl==3.0.7 32 | packaging==20.3 33 | pep517==0.8.2 34 | Pillow==7.2.0 35 | progress==1.5 36 | psycopg2-binary==2.8.5 37 | PyJWT==2.3.0 38 | pyparsing==2.4.6 39 | python-dateutil==2.8.2 40 | pytoml==0.1.21 41 | pytz==2021.1 42 | requests==2.22.0 43 | retrying==1.3.3 44 | ruamel.yaml==0.16.12 45 | ruamel.yaml.clib==0.2.2 46 | simplejson==3.17.2 47 | six==1.14.0 48 | sqlparse==0.4.1 49 | toml==0.10.1 50 | ua-parser==0.10.0 51 | uritemplate==3.0.1 52 | urllib3==1.25.8 53 | webencodings==0.5.1 54 | -------------------------------------------------------------------------------- /apps/products/api/serializers/general_serializers.py: -------------------------------------------------------------------------------- 1 | from apps.products.models import MeasureUnit,CategoryProduct,Indicator 2 | 3 | from rest_framework import serializers 4 | 5 | class MeasureUnitSerializer(serializers.ModelSerializer): 6 | 7 | class Meta: 8 | model = MeasureUnit 9 | exclude = ('state','created_date','modified_date','deleted_date') 10 | 11 | class CategoryProductSerializer(serializers.ModelSerializer): 12 | 13 | class Meta: 14 | model = CategoryProduct 15 | exclude = ('state','created_date','modified_date','deleted_date') 16 | 17 | class IndicatorSerializer(serializers.ModelSerializer): 18 | 19 | class Meta: 20 | model = Indicator 21 | exclude = ('state','created_date','modified_date','deleted_date') 22 | 23 | def to_representation(self, instance): 24 | return { 25 | 'id': instance.id, 26 | 'descount_value': instance.descount_value, 27 | 'category_product': instance.category_product.__str__() 28 | } 29 | 30 | class IndicatorUpdateSerializer(serializers.ModelSerializer): 31 | 32 | class Meta: 33 | model = Indicator 34 | exclude = ('state','created_date','modified_date','deleted_date') 35 | -------------------------------------------------------------------------------- /apps/users/authentication_mixins.py: -------------------------------------------------------------------------------- 1 | from rest_framework import status, authentication, exceptions 2 | from rest_framework.response import Response 3 | from rest_framework.renderers import JSONRenderer 4 | from rest_framework.authentication import get_authorization_header 5 | 6 | from apps.users.authentication import ExpiringTokenAuthentication 7 | 8 | class Authentication(authentication.BaseAuthentication): 9 | user = None 10 | 11 | def get_user(self,request): 12 | """ 13 | Return: 14 | * user : User Instance or 15 | * message : Error Message or 16 | * None : Corrup Token 17 | """ 18 | token = get_authorization_header(request).split() 19 | if token: 20 | try: 21 | token = token[1].decode() 22 | except: 23 | return None 24 | 25 | token_expire = ExpiringTokenAuthentication() 26 | user = token_expire.authenticate_credentials(token) 27 | 28 | if user != None: 29 | self.user = user 30 | return user 31 | 32 | return None 33 | 34 | def authenticate(self, request): 35 | self.get_user(request) 36 | if self.user is None: 37 | raise exceptions.AuthenticationFailed('No se han enviado las credenciales.') 38 | 39 | return (self.user, 1) 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /apps/products/migrations/0002_auto_20211104_2341.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.7 on 2021-11-04 23:41 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('products', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='historicalindicator', 15 | name='category_product', 16 | ), 17 | migrations.RemoveField( 18 | model_name='historicalindicator', 19 | name='history_user', 20 | ), 21 | migrations.RemoveField( 22 | model_name='historicalmeasureunit', 23 | name='history_user', 24 | ), 25 | migrations.RemoveField( 26 | model_name='historicalproduct', 27 | name='category_product', 28 | ), 29 | migrations.RemoveField( 30 | model_name='historicalproduct', 31 | name='history_user', 32 | ), 33 | migrations.RemoveField( 34 | model_name='historicalproduct', 35 | name='measure_unit', 36 | ), 37 | migrations.DeleteModel( 38 | name='HistoricalCategoryProduct', 39 | ), 40 | migrations.DeleteModel( 41 | name='HistoricalIndicator', 42 | ), 43 | migrations.DeleteModel( 44 | name='HistoricalMeasureUnit', 45 | ), 46 | migrations.DeleteModel( 47 | name='HistoricalProduct', 48 | ), 49 | ] 50 | -------------------------------------------------------------------------------- /apps/users/api/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from rest_framework_simplejwt.serializers import TokenObtainPairSerializer 3 | 4 | from apps.users.models import User 5 | 6 | class CustomTokenObtainPairSerializer(TokenObtainPairSerializer): 7 | pass 8 | 9 | class CustomUserSerializer(serializers.ModelSerializer): 10 | class Meta: 11 | model = User 12 | fields = ('username','email','name','last_name') 13 | 14 | class UserSerializer(serializers.ModelSerializer): 15 | class Meta: 16 | model = User 17 | fields = '__all__' 18 | 19 | def create(self,validated_data): 20 | user = User(**validated_data) 21 | user.set_password(validated_data['password']) 22 | user.save() 23 | return user 24 | 25 | class UpdateUserSerializer(serializers.ModelSerializer): 26 | class Meta: 27 | model = User 28 | fields = ('username', 'email', 'name', 'last_name') 29 | 30 | class PasswordSerializer(serializers.Serializer): 31 | password = serializers.CharField(max_length=128, min_length=6, write_only=True) 32 | password2 = serializers.CharField(max_length=128, min_length=6, write_only=True) 33 | 34 | def validate(self, data): 35 | if data['password'] != data['password2']: 36 | raise serializers.ValidationError( 37 | {'password':'Debe ingresar ambas contraseñas iguales'} 38 | ) 39 | return data 40 | 41 | class UserListSerializer(serializers.ModelSerializer): 42 | class Meta: 43 | model = User 44 | 45 | def to_representation(self, instance): 46 | return { 47 | 'id': instance['id'], 48 | 'name': instance['name'], 49 | 'username': instance['username'], 50 | 'email': instance['email'] 51 | } 52 | 53 | 54 | -------------------------------------------------------------------------------- /tests/tests_expense_manager/test_expense_viewset.py: -------------------------------------------------------------------------------- 1 | from apps.expense_manager.models import Supplier 2 | from rest_framework import status 3 | 4 | from tests.test_setup import TestSetUp 5 | from tests.factories.expense_manager.expense_factories import SupplierFactory 6 | 7 | class ExpenseTestCase(TestSetUp): 8 | url = '/expense/expense/' 9 | 10 | def test_search_supplier(self): 11 | supplier = SupplierFactory().create_supplier() 12 | response = self.client.get( 13 | self.url + 'search_supplier/', 14 | { 15 | 'ruc_or_business_name': supplier.ruc 16 | }, 17 | format='json' 18 | ) 19 | 20 | self.assertEqual(response.status_code, status.HTTP_200_OK) 21 | self.assertEqual(response.data['ruc'], supplier.ruc) 22 | 23 | def test_search_supplier_error(self): 24 | supplier = SupplierFactory().create_supplier() 25 | response = self.client.get( 26 | self.url + 'search_supplier/', 27 | { 28 | 'ruc_or_business_name': '43543543' 29 | }, 30 | format='json' 31 | ) 32 | 33 | self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) 34 | self.assertNotEqual(supplier.ruc, '43543543') 35 | self.assertEqual(response.data['mensaje'], 'No se ha encontrado un Proveedor.') 36 | 37 | def test_new_supplier(self): 38 | supplier = SupplierFactory().build_supplier_JSON() 39 | response = self.client.post( 40 | self.url + 'new_suplier/', 41 | supplier, 42 | format='json' 43 | ) 44 | 45 | self.assertEqual(response.status_code, status.HTTP_201_CREATED) 46 | self.assertEqual(Supplier.objects.all().count(), 1) 47 | self.assertEqual(response.data['supplier']['ruc'], supplier['ruc']) -------------------------------------------------------------------------------- /ecommerce_rest/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.urls import path,include,re_path 3 | from django.conf import settings 4 | from django.views.static import serve 5 | 6 | from rest_framework import permissions 7 | 8 | from drf_yasg.views import get_schema_view 9 | from drf_yasg import openapi 10 | 11 | from rest_framework_simplejwt.views import ( 12 | TokenObtainPairView, 13 | TokenRefreshView, 14 | ) 15 | 16 | from apps.users.views import Login,Logout 17 | 18 | schema_view = get_schema_view( 19 | openapi.Info( 20 | title="Documentación de API", 21 | default_version='v0.1', 22 | description="Documentación pública de API de Ecommerce", 23 | terms_of_service="https://www.google.com/policies/terms/", 24 | contact=openapi.Contact(email="developerpeperu@gmail.com"), 25 | license=openapi.License(name="BSD License"), 26 | ), 27 | public=True, 28 | permission_classes=(permissions.AllowAny,), 29 | ) 30 | 31 | urlpatterns = [ 32 | re_path(r'^swagger(?P\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'), 33 | path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), 34 | path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), 35 | path('admin/', admin.site.urls), 36 | path('logout/', Logout.as_view(), name = 'logout'), 37 | path('login/',Login.as_view(), name = 'login'), 38 | path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'), 39 | path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), 40 | path('users/',include('apps.users.api.routers')), 41 | path('products/',include('apps.products.api.routers')), 42 | path('expense/',include('apps.expense_manager.api.routers')), 43 | ] 44 | 45 | urlpatterns += [ 46 | re_path(r'^media/(?P.*)$', serve, { 47 | 'document_root': settings.MEDIA_ROOT, 48 | }), 49 | ] -------------------------------------------------------------------------------- /apps/users/authentication.py: -------------------------------------------------------------------------------- 1 | from datetime import timedelta 2 | 3 | from django.utils import timezone 4 | from django.conf import settings 5 | 6 | from rest_framework.response import Response 7 | from rest_framework.authentication import TokenAuthentication 8 | from rest_framework.exceptions import AuthenticationFailed 9 | 10 | class ExpiringTokenAuthentication(TokenAuthentication): 11 | 12 | def expires_in(self,token): 13 | # return left time of token 14 | time_elapsed = timezone.now() - token.created 15 | left_time = timedelta(seconds=settings.TOKEN_EXPIRED_AFTER_SECONDS) - time_elapsed 16 | return left_time 17 | 18 | def is_token_expired(self,token): 19 | # return True if token is alive or False if token is expired 20 | return self.expires_in(token) < timedelta(seconds = 0) 21 | 22 | def token_expire_handler(self,token): 23 | """ 24 | Return: 25 | * is_expire : True if token is alive, False if token is expired 26 | * token : New token or actual token 27 | """ 28 | is_expire = self.is_token_expired(token) 29 | if is_expire: 30 | user = token.user 31 | token.delete() 32 | token = self.get_model().objects.create(user=user) 33 | 34 | return token 35 | 36 | def authenticate_credentials(self,key): 37 | """ 38 | Return: 39 | * user : Instance User that sended request 40 | * token : New Token or actual token for user 41 | * message : Error message 42 | * expired : True if token is alive or False if token is expired 43 | """ 44 | user = None 45 | try: 46 | token = self.get_model().objects.select_related('user').get(key=key) 47 | token = self.token_expire_handler(token) 48 | user = token.user 49 | except self.get_model().DoesNotExist: 50 | pass 51 | 52 | return user 53 | 54 | -------------------------------------------------------------------------------- /apps/users/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import BaseUserManager, AbstractBaseUser, PermissionsMixin 3 | from simple_history.models import HistoricalRecords 4 | 5 | 6 | class UserManager(BaseUserManager): 7 | def _create_user(self, username, email, name,last_name, password, is_staff, is_superuser, **extra_fields): 8 | user = self.model( 9 | username = username, 10 | email = email, 11 | name = name, 12 | last_name = last_name, 13 | is_staff = is_staff, 14 | is_superuser = is_superuser, 15 | **extra_fields 16 | ) 17 | user.set_password(password) 18 | user.save(using=self.db) 19 | return user 20 | 21 | def create_user(self, username, email, name,last_name, password=None, **extra_fields): 22 | return self._create_user(username, email, name,last_name, password, False, False, **extra_fields) 23 | 24 | def create_superuser(self, username, email, name,last_name, password=None, **extra_fields): 25 | return self._create_user(username, email, name,last_name, password, True, True, **extra_fields) 26 | 27 | class User(AbstractBaseUser, PermissionsMixin): 28 | username = models.CharField(max_length = 255, unique = True) 29 | email = models.EmailField('Correo Electrónico',max_length = 255, unique = True,) 30 | name = models.CharField('Nombres', max_length = 255, blank = True, null = True) 31 | last_name = models.CharField('Apellidos', max_length = 255, blank = True, null = True) 32 | image = models.ImageField('Imagen de perfil', upload_to='perfil/', max_length=255, null=True, blank = True) 33 | is_active = models.BooleanField(default = True) 34 | is_staff = models.BooleanField(default = False) 35 | historical = HistoricalRecords() 36 | objects = UserManager() 37 | 38 | class Meta: 39 | verbose_name = 'Usuario' 40 | verbose_name_plural = 'Usuarios' 41 | 42 | USERNAME_FIELD = 'username' 43 | REQUIRED_FIELDS = ['email','name','last_name'] 44 | 45 | def __str__(self): 46 | return f'{self.name} {self.last_name}' 47 | -------------------------------------------------------------------------------- /apps/users/views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import authenticate 2 | 3 | from rest_framework import status 4 | from rest_framework.generics import GenericAPIView 5 | from rest_framework.response import Response 6 | 7 | from rest_framework_simplejwt.tokens import RefreshToken 8 | from rest_framework_simplejwt.views import TokenObtainPairView 9 | 10 | from apps.users.api.serializers import ( 11 | CustomTokenObtainPairSerializer, CustomUserSerializer 12 | ) 13 | from apps.users.models import User 14 | 15 | 16 | 17 | class Login(TokenObtainPairView): 18 | serializer_class = CustomTokenObtainPairSerializer 19 | 20 | def post(self, request, *args, **kwargs): 21 | username = request.data.get('username', '') 22 | password = request.data.get('password', '') 23 | user = authenticate( 24 | username=username, 25 | password=password 26 | ) 27 | 28 | if user: 29 | login_serializer = self.serializer_class(data=request.data) 30 | if login_serializer.is_valid(): 31 | user_serializer = CustomUserSerializer(user) 32 | return Response({ 33 | 'token': login_serializer.validated_data.get('access'), 34 | 'refresh-token': login_serializer.validated_data.get('refresh'), 35 | 'user': user_serializer.data, 36 | 'message': 'Inicio de Sesion Existoso' 37 | }, status=status.HTTP_200_OK) 38 | return Response({'error': 'Contraseña o nombre de usuario incorrectos'}, status=status.HTTP_400_BAD_REQUEST) 39 | return Response({'error': 'Contraseña o nombre de usuario incorrectos'}, status=status.HTTP_400_BAD_REQUEST) 40 | 41 | class Logout(GenericAPIView): 42 | def post(self, request, *args, **kwargs): 43 | user = User.objects.filter(id=request.data.get('user', 0)) 44 | if user.exists(): 45 | RefreshToken.for_user(user.first()) 46 | return Response({'message': 'Sesión cerrada correctamente.'}, status=status.HTTP_200_OK) 47 | return Response({'error': 'No existe este usuario.'}, status=status.HTTP_400_BAD_REQUEST) -------------------------------------------------------------------------------- /apps/products/api/serializers/product_serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | 3 | from apps.products.models import Product 4 | from apps.products.api.serializers.general_serializers import ( 5 | MeasureUnitSerializer,CategoryProductSerializer 6 | ) 7 | 8 | class ProductSerializer(serializers.ModelSerializer): 9 | 10 | class Meta: 11 | model = Product 12 | exclude = ('stock', 'state','created_date','modified_date','deleted_date') 13 | 14 | def validate_measure_unit(self, value): 15 | if value == '' or value == None: 16 | raise serializers.ValidationError("Debe ingresar una Unidad de Medida.") 17 | return value 18 | 19 | def validate_category_product(self, value): 20 | if value == '' or value == None: 21 | raise serializers.ValidationError("Debe ingresar una Categoría de Producto") 22 | return value 23 | 24 | def validate(self, data): 25 | if 'measure_unit' not in data.keys(): 26 | raise serializers.ValidationError({ 27 | "measure_unit": "Debe ingresar una Unidad de Medida." 28 | }) 29 | 30 | if 'category_product' not in data.keys(): 31 | raise serializers.ValidationError({ 32 | "category_product": "Debe ingresar una Categoria de Producto." 33 | }) 34 | 35 | return data 36 | 37 | def to_representation(self,instance): 38 | return { 39 | 'id': instance.id, 40 | 'stock': instance.stock.get('quantity__sum') if instance.stock.get('quantity__sum') is not None else 0, 41 | 'name': instance.name, 42 | 'description': instance.description, 43 | 'image': instance.image.url if instance.image != '' else '', 44 | 'measure_unit': instance.measure_unit.description if instance.measure_unit is not None else '', 45 | 'category_product': instance.category_product.description if instance.category_product is not None else '' 46 | } 47 | 48 | class ProductRetrieveSerializer(serializers.ModelSerializer): 49 | 50 | class Meta: 51 | model = Product 52 | exclude = ('state','created_date','modified_date','deleted_date') 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/django 3 | # Edit at https://www.gitignore.io/?templates=django 4 | 5 | ### Django ### 6 | *.log 7 | *.pot 8 | *.pyc 9 | __pycache__ 10 | .vscode/ 11 | .env 12 | #db.sqlite3 13 | 14 | 15 | #0*.py 16 | # If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/ 17 | # in your Git repository. Update and uncomment the following line accordingly. 18 | # /staticfiles/ 19 | 20 | ### Django.Python Stack ### 21 | # Byte-compiled / optimized / DLL files 22 | *.py[cod] 23 | *$py.class 24 | 25 | # C extensions 26 | *.so 27 | 28 | # Distribution / packaging 29 | .Python 30 | develop-eggs/ 31 | downloads/ 32 | eggs/ 33 | .eggs/ 34 | parts/ 35 | wheels/ 36 | share/python-wheels/ 37 | *.egg-info/ 38 | .installed.cfg 39 | *.egg 40 | MANIFEST 41 | 42 | # PyInstaller 43 | # Usually these files are written by a python script from a template 44 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 45 | *.manifest 46 | *.spec 47 | 48 | # Installer logs 49 | pip-log.txt 50 | pip-delete-this-directory.txt 51 | 52 | # Unit test / coverage reports 53 | htmlcov/ 54 | .tox/ 55 | .nox/ 56 | .coverage 57 | .coverage.* 58 | .cache 59 | nosetests.xml 60 | coverage.xml 61 | *.cover 62 | .hypothesis/ 63 | .pytest_cache/ 64 | 65 | # Translations 66 | *.mo 67 | 68 | # Django stuff: 69 | 70 | # Flask stuff: 71 | instance/ 72 | .webassets-cache 73 | 74 | # Scrapy stuff: 75 | .scrapy 76 | 77 | # Sphinx documentation 78 | docs/_build/ 79 | 80 | # PyBuilder 81 | target/ 82 | 83 | # Jupyter Notebook 84 | .ipynb_checkpoints 85 | 86 | # IPython 87 | profile_default/ 88 | ipython_config.py 89 | 90 | # pyenv 91 | .python-version 92 | 93 | # celery beat schedule file 94 | celerybeat-schedule 95 | 96 | # SageMath parsed files 97 | *.sage.py 98 | 99 | # Environments 100 | .env 101 | .venv 102 | env/ 103 | venv/ 104 | ENV/ 105 | env.bak/ 106 | venv.bak/ 107 | 108 | # Spyder project settings 109 | .spyderproject 110 | .spyproject 111 | 112 | # Rope project settings 113 | .ropeproject 114 | 115 | # mkdocs documentation 116 | /site 117 | 118 | # mypy 119 | .mypy_cache/ 120 | .dmypy.json 121 | dmypy.json 122 | 123 | # Pyre type checker 124 | .pyre/ 125 | 126 | # End of https://www.gitignore.io/api/django 127 | 128 | development.py 129 | #local.pynueva 130 | -------------------------------------------------------------------------------- /apps/products/api/viewsets/product_viewsets.py: -------------------------------------------------------------------------------- 1 | from rest_framework import status 2 | from rest_framework import viewsets 3 | from rest_framework.response import Response 4 | from rest_framework.parsers import JSONParser, MultiPartParser 5 | 6 | from apps.base.utils import validate_files 7 | from apps.products.api.serializers.product_serializers import ( 8 | ProductSerializer, ProductRetrieveSerializer 9 | ) 10 | 11 | class ProductViewSet(viewsets.ModelViewSet): 12 | serializer_class = ProductSerializer 13 | parser_classes = (JSONParser, MultiPartParser, ) 14 | 15 | def get_queryset(self, pk=None): 16 | if pk is None: 17 | return self.get_serializer().Meta.model.objects.filter(state=True) 18 | return self.get_serializer().Meta.model.objects.filter(id=pk, state=True).first() 19 | 20 | def list(self, request): 21 | product_serializer = self.get_serializer(self.get_queryset(), many=True) 22 | data = { 23 | "total": self.get_queryset().count(), 24 | "totalNotFiltered": self.get_queryset().count(), 25 | "rows": product_serializer.data 26 | } 27 | return Response(data, status=status.HTTP_200_OK) 28 | 29 | def create(self, request): 30 | # send information to serializer 31 | data = validate_files(request.data,'image') 32 | serializer = self.serializer_class(data=data) 33 | if serializer.is_valid(): 34 | serializer.save() 35 | return Response({'message': 'Producto creado correctamente!'}, status=status.HTTP_201_CREATED) 36 | return Response({'message':'', 'error':serializer.errors}, status=status.HTTP_400_BAD_REQUEST) 37 | 38 | def retrieve(self, request, pk=None): 39 | product = self.get_queryset(pk) 40 | if product: 41 | product_serializer = ProductRetrieveSerializer(product) 42 | return Response(product_serializer.data, status=status.HTTP_200_OK) 43 | return Response({'error':'No existe un Producto con estos datos!'}, status=status.HTTP_400_BAD_REQUEST) 44 | 45 | def update(self, request, pk=None): 46 | if self.get_queryset(pk): 47 | # send information to serializer referencing the instance 48 | data = validate_files(request.data, 'image', True) 49 | product_serializer = self.serializer_class(self.get_queryset(pk), data=data) 50 | if product_serializer.is_valid(): 51 | product_serializer.save() 52 | return Response({'message':'Producto actualizado correctamente!'}, status=status.HTTP_200_OK) 53 | return Response({'message':'', 'error':product_serializer.errors}, status=status.HTTP_400_BAD_REQUEST) 54 | 55 | def destroy(self, request, pk=None): 56 | product = self.get_queryset().filter(id=pk).first() # get instance 57 | if product: 58 | product.state = False 59 | product.save() 60 | return Response({'message':'Producto eliminado correctamente!'}, status=status.HTTP_200_OK) 61 | return Response({'error':'No existe un Producto con estos datos!'}, status=status.HTTP_400_BAD_REQUEST) 62 | -------------------------------------------------------------------------------- /apps/products/models.py: -------------------------------------------------------------------------------- 1 | from itertools import product 2 | from django.db import models 3 | 4 | from apps.base.models import BaseModel 5 | # Create your models here. 6 | 7 | class MeasureUnit(BaseModel): 8 | """Model definition for MeasureUnit.""" 9 | 10 | # TODO: Define fields here 11 | description = models.CharField('Descripción', max_length=50, blank=False, null=False, unique=True) 12 | 13 | class Meta: 14 | """Meta definition for MeasureUnit.""" 15 | 16 | verbose_name = 'Unidad de Medida' 17 | verbose_name_plural = 'Unidades de Medidas' 18 | 19 | def __str__(self): 20 | """Unicode representation of MeasureUnit.""" 21 | return self.description 22 | 23 | class CategoryProduct(BaseModel): 24 | """Model definition for CategoryProduct.""" 25 | 26 | # TODO: Define fields here 27 | description = models.CharField('Descripcion', max_length=50, unique=True, null=False, blank=False) 28 | 29 | class Meta: 30 | """Meta definition for CategoryProduct.""" 31 | 32 | verbose_name = 'Categoría de Producto' 33 | verbose_name_plural = 'Categorías de Productos' 34 | 35 | def __str__(self): 36 | """Unicode representation of CategoryProduct.""" 37 | return self.description 38 | 39 | class Indicator(BaseModel): 40 | """Model definition for Indicator.""" 41 | 42 | # TODO: Define fields here 43 | descount_value = models.PositiveSmallIntegerField(default=0) 44 | category_product = models.ForeignKey(CategoryProduct, on_delete=models.CASCADE, verbose_name='Indicador de Oferta') 45 | 46 | class Meta: 47 | """Meta definition for Indicator.""" 48 | 49 | verbose_name = 'Indicador de Oferta' 50 | verbose_name_plural = 'Indicadores de Ofertas' 51 | 52 | def __str__(self): 53 | """Unicode representation of Indicator.""" 54 | return f'Oferta de la categoría {self.category_product} : {self.descount_value}%' 55 | 56 | class Product(BaseModel): 57 | """Model definition for Product.""" 58 | 59 | # TODO: Define fields here 60 | name = models.CharField('Nombre de Producto', max_length=150, unique=True, blank=False, null=False) 61 | description = models.TextField('Descripción de Producto', blank=False, null=False) 62 | image = models.ImageField('Imagen del Producto', upload_to='products/', blank=True, null=True) 63 | measure_unit = models.ForeignKey(MeasureUnit, on_delete=models.CASCADE, verbose_name='Unidad de Medida', null=True) 64 | category_product = models.ForeignKey(CategoryProduct, on_delete=models.CASCADE, verbose_name='Categoria de Producto', null=True) 65 | 66 | class Meta: 67 | """Meta definition for Product.""" 68 | 69 | verbose_name = 'Producto' 70 | verbose_name_plural = 'Productos' 71 | 72 | def __str__(self): 73 | """Unicode representation of Product.""" 74 | return self.name 75 | 76 | @property 77 | def stock(self): 78 | from django.db.models import Sum 79 | from apps.expense_manager.models import Expense 80 | 81 | expenses = Expense.objects.filter( 82 | product=self, 83 | state=True 84 | ).aggregate(Sum('quantity')) 85 | 86 | return expenses 87 | -------------------------------------------------------------------------------- /apps/users/api/api.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import get_object_or_404 2 | 3 | from rest_framework import status 4 | from rest_framework.response import Response 5 | from rest_framework.decorators import action 6 | from rest_framework import viewsets 7 | 8 | from apps.users.models import User 9 | from apps.users.api.serializers import ( 10 | UserSerializer, UserListSerializer, UpdateUserSerializer, 11 | PasswordSerializer 12 | ) 13 | 14 | class UserViewSet(viewsets.GenericViewSet): 15 | model = User 16 | serializer_class = UserSerializer 17 | list_serializer_class = UserListSerializer 18 | queryset = None 19 | 20 | def get_object(self, pk): 21 | return get_object_or_404(self.model, pk=pk) 22 | 23 | def get_queryset(self): 24 | if self.queryset is None: 25 | self.queryset = self.model.objects\ 26 | .filter(is_active=True)\ 27 | .values('id', 'username', 'email', 'name') 28 | return self.queryset 29 | 30 | @action(detail=True, methods=['post']) 31 | def set_password(self, request, pk=None): 32 | user = self.get_object(pk) 33 | password_serializer = PasswordSerializer(data=request.data) 34 | if password_serializer.is_valid(): 35 | user.set_password(password_serializer.validated_data['password']) 36 | user.save() 37 | return Response({ 38 | 'message': 'Contraseña actualizada correctamente' 39 | }) 40 | return Response({ 41 | 'message': 'Hay errores en la información enviada', 42 | 'errors': password_serializer.errors 43 | }, status=status.HTTP_400_BAD_REQUEST) 44 | 45 | def list(self, request): 46 | users = self.get_queryset() 47 | users_serializer = self.list_serializer_class(users, many=True) 48 | return Response(users_serializer.data, status=status.HTTP_200_OK) 49 | 50 | def create(self, request): 51 | user_serializer = self.serializer_class(data=request.data) 52 | if user_serializer.is_valid(): 53 | user_serializer.save() 54 | return Response({ 55 | 'message': 'Usuario registrado correctamente.' 56 | }, status=status.HTTP_201_CREATED) 57 | return Response({ 58 | 'message': 'Hay errores en el registro', 59 | 'errors': user_serializer.errors 60 | }, status=status.HTTP_400_BAD_REQUEST) 61 | 62 | def retrieve(self, request, pk=None): 63 | user = self.get_object(pk) 64 | user_serializer = self.serializer_class(user) 65 | return Response(user_serializer.data) 66 | 67 | def update(self, request, pk=None): 68 | user = self.get_object(pk) 69 | user_serializer = UpdateUserSerializer(user, data=request.data) 70 | if user_serializer.is_valid(): 71 | user_serializer.save() 72 | return Response({ 73 | 'message': 'Usuario actualizado correctamente' 74 | }, status=status.HTTP_200_OK) 75 | return Response({ 76 | 'message': 'Hay errores en la actualización', 77 | 'errors': user_serializer.errors 78 | }, status=status.HTTP_400_BAD_REQUEST) 79 | 80 | def destroy(self, request, pk=None): 81 | user_destroy = self.model.objects.filter(id=pk).update(is_active=False) 82 | if user_destroy == 1: 83 | return Response({ 84 | 'message': 'Usuario eliminado correctamente' 85 | }) 86 | return Response({ 87 | 'message': 'No existe el usuario que desea eliminar' 88 | }, status=status.HTTP_404_NOT_FOUND) 89 | -------------------------------------------------------------------------------- /apps/expense_manager/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | from apps.base.models import BaseModel 4 | from apps.products.models import Product 5 | 6 | class Supplier(BaseModel): 7 | ruc = models.CharField(unique=True, max_length=11) 8 | business_name = models.CharField('Razón Social', unique=True, max_length=150, null=False, blank=False) 9 | address = models.CharField(max_length=200) 10 | phone = models.CharField(max_length=15, null=True, blank=True) 11 | email = models.EmailField(null=True) 12 | 13 | class Meta: 14 | ordering = ['id'] 15 | verbose_name = 'Proveedor' 16 | verbose_name_plural = 'Proveedores' 17 | 18 | def __str__(self): 19 | return self.business_name 20 | 21 | def to_dict(self): 22 | return { 23 | 'id': self.id, 24 | 'ruc': self.ruc, 25 | 'business_name': self.business_name, 26 | 'address': self.address, 27 | 'phone': self.phone, 28 | 'email': self.email 29 | } 30 | 31 | class PaymentType(BaseModel): 32 | name = models.CharField('Nombre de Medio de Pago', max_length=100) 33 | 34 | class Meta: 35 | ordering = ['id'] 36 | verbose_name = 'Medio de Pago' 37 | verbose_name_plural = 'Medio de Pagos' 38 | 39 | def __str__(self): 40 | return self.name 41 | 42 | 43 | class Voucher(BaseModel): 44 | name = models.CharField('Nombre de comprobante de Pago', max_length=100) 45 | 46 | class Meta: 47 | ordering = ['id'] 48 | verbose_name = 'Comprobante' 49 | verbose_name_plural = 'Comprobantes' 50 | 51 | def __str__(self): 52 | return self.name 53 | 54 | 55 | class ExpenseCategory(BaseModel): 56 | name = models.CharField('Nombre de Categoría de Gasto', max_length=100) 57 | 58 | class Meta: 59 | ordering = ['id'] 60 | verbose_name = 'Categoria de Gasto' 61 | verbose_name_plural = 'Categorias de Gastos' 62 | 63 | def __str__(self): 64 | return self.name 65 | 66 | class Expense(BaseModel): 67 | date = models.DateField('Fecha de emisión de factura', auto_now=False, auto_now_add=False) 68 | quantity = models.DecimalField('Cantidad', max_digits=10, decimal_places=2) 69 | unit_price = models.DecimalField('Precio Unitario', max_digits=10, decimal_places=2, default=0) 70 | voucher_number = models.CharField('Número de comprobante', max_length=50) 71 | total = models.DecimalField('Total', max_digits=10, decimal_places=2, default=0) 72 | voucher = models.ForeignKey(Voucher, on_delete=models.CASCADE) 73 | user = models.ForeignKey("users.User", on_delete=models.CASCADE) 74 | supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE) 75 | payment_type = models.ForeignKey(PaymentType, on_delete=models.CASCADE) 76 | product = models.ForeignKey(Product, on_delete=models.CASCADE) 77 | 78 | class Meta: 79 | ordering = ['id'] 80 | verbose_name = 'Gasto' 81 | verbose_name_plural = 'Gastos' 82 | 83 | def __str__(self): 84 | return self.voucher_number 85 | 86 | class Merma(BaseModel): 87 | date = models.DateField('Fecha de emisión de Merma', auto_now=False, auto_now_add=False) 88 | product = models.ForeignKey(Product, on_delete=models.CASCADE) 89 | quantity = models.DecimalField('Cantidad', max_digits=7, decimal_places=2) 90 | lost_money = models.DecimalField('Dinero perdido', max_digits=7, decimal_places=2) 91 | 92 | class Meta: 93 | ordering = ['id'] 94 | verbose_name = 'Merma' 95 | verbose_name_plural = 'Mermas' 96 | 97 | 98 | def __str__(self): 99 | return "Merma de {0}".format(self.product.__str__()) -------------------------------------------------------------------------------- /apps/expense_manager/api/viewsets/expense_viewsets.py: -------------------------------------------------------------------------------- 1 | from django.db.models import Q 2 | 3 | from rest_framework import status 4 | from rest_framework import viewsets 5 | from rest_framework.decorators import action 6 | from rest_framework.response import Response 7 | 8 | from rest_framework_simplejwt.authentication import JWTAuthentication 9 | 10 | from apps.base.utils import format_date 11 | from apps.products.models import Product 12 | from apps.expense_manager.models import ( 13 | Supplier, Voucher, PaymentType 14 | ) 15 | 16 | from apps.expense_manager.api.serializers.general_serializer import * 17 | from apps.expense_manager.api.serializers.expense_serializer import * 18 | 19 | class ExpenseViewSet(viewsets.GenericViewSet): 20 | serializer_class = ExpenseSerializer 21 | 22 | @action(methods=['get'], detail=False) 23 | def search_supplier(self, request): 24 | ruc_or_business_name = request.query_params.get('ruc_or_business_name', '') 25 | supplier = Supplier.objects.filter( 26 | Q(ruc__iexact=ruc_or_business_name)| 27 | Q(business_name__iexact=ruc_or_business_name) 28 | ).first() 29 | if supplier: 30 | supplier_serializer = SupplierSerializer(supplier) 31 | return Response(supplier_serializer.data, status=status.HTTP_200_OK) 32 | return Response({ 33 | 'mensaje': 'No se ha encontrado un Proveedor.' 34 | }, status=status.HTTP_400_BAD_REQUEST) 35 | 36 | @action(methods=['post'], detail=False) 37 | def new_suplier(self ,request): 38 | data_supplier = request.data 39 | data_supplier = SupplierRegisterSerializer(data=data_supplier) 40 | if data_supplier.is_valid(): 41 | data_supplier = data_supplier.save() 42 | return Response({ 43 | 'message:': 'Proveedor registrado correctamente!', 44 | 'supplier': data_supplier 45 | }, status=status.HTTP_201_CREATED) 46 | return Response({'error': data_supplier.errors}, status=status.HTTP_400_BAD_REQUEST) 47 | 48 | @action(methods=['get'], detail=False) 49 | def get_vouchers(self, request): 50 | data = Voucher.objects.filter(state=True).order_by('id') 51 | data = VoucherSerializer(data, many=True).data 52 | return Response(data) 53 | 54 | @action(methods=['get'], detail=False) 55 | def get_payment_types(self, request): 56 | data = PaymentType.objects.filter(state=True).order_by('id') 57 | data = PaymentTypeSerializer(data, many=True).data 58 | return Response(data) 59 | 60 | @action(methods=['get'], detail=False) 61 | def get_products(self, request): 62 | data = Product.objects.filter(state=True).order_by('id') 63 | data = ProductSerializer(data, many=True).data 64 | return Response(data) 65 | 66 | def format_data(self, data): 67 | JWT_authenticator = JWTAuthentication() 68 | # decode token 69 | user, _ = JWT_authenticator.authenticate(self.request) 70 | data['user'] = user.id 71 | data['date'] = format_date(data['date']) 72 | return data 73 | 74 | def create(self, request): 75 | data = self.format_data(request.data) 76 | serializer = self.serializer_class(data=data) 77 | if serializer.is_valid(): 78 | serializer.save() 79 | return Response({ 80 | 'message': 'Factura registrada correctamente.' 81 | }, status=status.HTTP_201_CREATED) 82 | else: 83 | return Response({ 84 | 'message': 'Han ocurrido errores en la creación.', 85 | 'errors': serializer.errors 86 | }, status=status.HTTP_400_BAD_REQUEST) 87 | -------------------------------------------------------------------------------- /ecommerce_rest/settings/base.py: -------------------------------------------------------------------------------- 1 | from datetime import timedelta 2 | from pathlib import Path 3 | 4 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 5 | BASE_DIR = Path(__file__).resolve().parent.parent 6 | 7 | 8 | # Quick-start development settings - unsuitable for production 9 | # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ 10 | 11 | # SECURITY WARNING: keep the secret key used in production secret! 12 | SECRET_KEY = 'e+3qek7(i5evq)87ff5d8e@bjsd&q_h)w5qejoojqhhx%$4j+h' 13 | 14 | # SECURITY WARNING: don't run with debug turned on in production! 15 | DEBUG = True 16 | 17 | ALLOWED_HOSTS = [] 18 | 19 | 20 | # Application definition 21 | 22 | BASE_APPS = [ 23 | 'django.contrib.admin', 24 | 'django.contrib.auth', 25 | 'django.contrib.contenttypes', 26 | 'django.contrib.sessions', 27 | 'django.contrib.messages', 28 | 'django.contrib.staticfiles', 29 | ] 30 | 31 | LOCAL_APPS = [ 32 | 'apps.base', 33 | 'apps.users', 34 | 'apps.products', 35 | 'apps.expense_manager', 36 | ] 37 | 38 | THIRD_APPS = [ 39 | 'corsheaders', 40 | 'automatic_crud', 41 | 'rest_framework', 42 | #'rest_framework.authtoken', 43 | 'rest_framework_simplejwt', 44 | 'rest_framework_simplejwt.token_blacklist', 45 | 'simple_history', 46 | 'drf_yasg', 47 | ] 48 | 49 | INSTALLED_APPS = BASE_APPS + LOCAL_APPS + THIRD_APPS 50 | 51 | SWAGGER_SETTINGS = { 52 | 'DOC_EXPANSION': 'none' 53 | } 54 | 55 | REST_FRAMEWORK = { 56 | 'DEFAULT_AUTHENTICATION_CLASSES': [ 57 | 'rest_framework_simplejwt.authentication.JWTAuthentication', 58 | ], 59 | 'DEFAULT_PERMISSION_CLASSES': ( 60 | 'rest_framework.permissions.IsAuthenticated', 61 | ) 62 | } 63 | 64 | MIDDLEWARE = [ 65 | 'django.middleware.security.SecurityMiddleware', 66 | 'django.contrib.sessions.middleware.SessionMiddleware', 67 | 'corsheaders.middleware.CorsMiddleware', 68 | 'django.middleware.common.CommonMiddleware', 69 | 'django.middleware.csrf.CsrfViewMiddleware', 70 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 71 | 'django.contrib.messages.middleware.MessageMiddleware', 72 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 73 | 'simple_history.middleware.HistoryRequestMiddleware', 74 | ] 75 | 76 | ROOT_URLCONF = 'ecommerce_rest.urls' 77 | 78 | TEMPLATES = [ 79 | { 80 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 81 | 'DIRS': [], 82 | 'APP_DIRS': True, 83 | 'OPTIONS': { 84 | 'context_processors': [ 85 | 'django.template.context_processors.debug', 86 | 'django.template.context_processors.request', 87 | 'django.contrib.auth.context_processors.auth', 88 | 'django.contrib.messages.context_processors.messages', 89 | ], 90 | }, 91 | }, 92 | ] 93 | 94 | WSGI_APPLICATION = 'ecommerce_rest.wsgi.application' 95 | 96 | 97 | # Password validation 98 | # https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators 99 | 100 | AUTH_PASSWORD_VALIDATORS = [ 101 | { 102 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 103 | }, 104 | { 105 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 106 | }, 107 | { 108 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 109 | }, 110 | { 111 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 112 | }, 113 | ] 114 | 115 | 116 | # Internationalization 117 | # https://docs.djangoproject.com/en/3.1/topics/i18n/ 118 | 119 | LANGUAGE_CODE = 'es' 120 | 121 | TIME_ZONE = 'UTC' 122 | 123 | USE_I18N = True 124 | 125 | USE_L10N = True 126 | 127 | USE_TZ = True 128 | 129 | AUTH_USER_MODEL = 'users.User' 130 | 131 | CORS_ORIGIN_WHITELIST = [ 132 | "http://localhost:8000", 133 | "http://localhost:3000" 134 | ] 135 | 136 | SIMPLE_JWT = { 137 | 'ACCESS_TOKEN_LIFETIME': timedelta(days=1), 138 | 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), 139 | 'ROTATE_REFRESH_TOKENS': True, 140 | 'BLACKLIST_AFTER_ROTATION': True 141 | } 142 | # Static files (CSS, JavaScript, Images) 143 | # https://docs.djangoproject.com/en/3.1/howto/static-files/ 144 | 145 | STATIC_URL = '/static/' 146 | -------------------------------------------------------------------------------- /apps/users/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.7 on 2021-04-21 22:57 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | import simple_history.models 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | ('auth', '0012_alter_user_first_name_max_length'), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='User', 20 | fields=[ 21 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 22 | ('password', models.CharField(max_length=128, verbose_name='password')), 23 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), 24 | ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), 25 | ('username', models.CharField(max_length=255, unique=True)), 26 | ('email', models.EmailField(max_length=255, unique=True, verbose_name='Correo Electrónico')), 27 | ('name', models.CharField(blank=True, max_length=255, null=True, verbose_name='Nombres')), 28 | ('last_name', models.CharField(blank=True, max_length=255, null=True, verbose_name='Apellidos')), 29 | ('image', models.ImageField(blank=True, max_length=255, null=True, upload_to='perfil/', verbose_name='Imagen de perfil')), 30 | ('is_active', models.BooleanField(default=True)), 31 | ('is_staff', models.BooleanField(default=False)), 32 | ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), 33 | ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), 34 | ], 35 | options={ 36 | 'verbose_name': 'Usuario', 37 | 'verbose_name_plural': 'Usuarios', 38 | }, 39 | ), 40 | migrations.CreateModel( 41 | name='HistoricalUser', 42 | fields=[ 43 | ('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), 44 | ('password', models.CharField(max_length=128, verbose_name='password')), 45 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), 46 | ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), 47 | ('username', models.CharField(db_index=True, max_length=255)), 48 | ('email', models.EmailField(db_index=True, max_length=255, verbose_name='Correo Electrónico')), 49 | ('name', models.CharField(blank=True, max_length=255, null=True, verbose_name='Nombres')), 50 | ('last_name', models.CharField(blank=True, max_length=255, null=True, verbose_name='Apellidos')), 51 | ('image', models.TextField(blank=True, max_length=255, null=True, verbose_name='Imagen de perfil')), 52 | ('is_active', models.BooleanField(default=True)), 53 | ('is_staff', models.BooleanField(default=False)), 54 | ('history_id', models.AutoField(primary_key=True, serialize=False)), 55 | ('history_date', models.DateTimeField()), 56 | ('history_change_reason', models.CharField(max_length=100, null=True)), 57 | ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), 58 | ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), 59 | ], 60 | options={ 61 | 'verbose_name': 'historical Usuario', 62 | 'ordering': ('-history_date', '-history_id'), 63 | 'get_latest_by': 'history_date', 64 | }, 65 | bases=(simple_history.models.HistoricalChanges, models.Model), 66 | ), 67 | ] 68 | -------------------------------------------------------------------------------- /da: -------------------------------------------------------------------------------- 1 | commit 96835c8b47e0975a056e4177e25c41e9817dd846 (HEAD -> master, origin/master, origin/HEAD) 2 | Author: Oliver Sandoval 3 | Date: Fri Sep 3 20:17:16 2021 -0500 4 | 5 | Como funciona la Authenticacion en Django Rest Framework 6 | 7 | commit 7e824ec45be29f7b6c5419fa9255139cb6142c9e 8 | Author: Oliver Sandoval 9 | Date: Wed Jul 14 19:02:29 2021 -0500 10 | 11 | actualizar y eliminar categoria de producto agregado 12 | 13 | commit d7a9bcb3473f5d46b5900f6cba41e082a8c18c83 14 | Author: Oliver Sandoval 15 | Date: Wed Jul 14 17:55:50 2021 -0500 16 | 17 | eliminacion de categoria de producto agregada 18 | 19 | commit 99ca11a7c9d483ab8040b1afd5879752fa3c69bb 20 | Author: Oliver Sandoval 21 | Date: Thu Jun 10 19:28:06 2021 -0500 22 | 23 | registrar categoria de productos agregada 24 | 25 | commit 82252a0aececa9fb6e8c990104691abf9d7b981b 26 | Author: Oliver Sandoval 27 | Date: Sun May 16 19:45:43 2021 -0500 28 | 29 | agregado ruta de CORS, corregida ruta de Refresh Token 30 | 31 | commit 49897a30a055d46817c6f530da593954edba8a28 32 | Author: Oliver Sandoval 33 | Date: Mon May 3 00:18:55 2021 -0500 34 | 35 | cors-headers agregado 36 | 37 | commit a549c89fc2865b5b49548a30b75865ccfe219e65 38 | Author: Oliver Sandoval 39 | Date: Tue Mar 9 19:47:04 2021 -0500 40 | 41 | menor errors in gitignore 42 | 43 | commit 43f9607ddde7f1e1f31f44b4e86a9629b71b6fe7 44 | Author: Oliver Sandoval 45 | Date: Tue Mar 9 19:46:09 2021 -0500 46 | 47 | delete cache 48 | 49 | commit 7eb694ecdb15f1a2866ef00ef0cbf0a56d3c9d3f 50 | Author: Oliver Sandoval 51 | Date: Tue Mar 9 19:44:18 2021 -0500 52 | 53 | delete __pycache__ 54 | 55 | commit 6113e2108583fefcad7b9ab47b875fd108f69fc7 56 | Author: Oliver Sandoval 57 | Date: Tue Mar 9 19:42:04 2021 -0500 58 | 59 | add gitignore 60 | 61 | commit 4870299ec7f46bb3ef0f9490bdd22e76ef3b2cf5 62 | Author: Oliver Sandoval 63 | Date: Tue Mar 9 19:40:51 2021 -0500 64 | 65 | delete comments in general_viewsets 66 | 67 | commit 0533a7287f8123bb1bd486300d45280e25ebeb64 68 | Author: Oliver Sandoval 69 | Date: Tue Mar 9 19:37:34 2021 -0500 70 | 71 | README 72 | 73 | commit 406aa92984dcfad44bb11e6394cdced65e2ff4fe 74 | Author: Oliver Sandoval 75 | Date: Tue Mar 9 19:36:51 2021 -0500 76 | 77 | menor errors in README 78 | 79 | commit ae882778b7e0778f0050b98e242cb9c669c256ee 80 | Author: Oliver Sandoval 81 | Date: Tue Mar 9 19:36:07 2021 -0500 82 | 83 | menor errors in README 84 | 85 | commit a61584a98fd20201d638aa2f8103fa3cbeedf9f5 86 | Author: Oliver Sandoval 87 | Date: Tue Mar 9 19:35:08 2021 -0500 88 | 89 | add requirements 90 | 91 | commit 7dd2b1cda83200beaa45384e091b5ef6feea2be8 92 | Author: Oliver Sandoval 93 | Date: Tue Mar 9 19:34:18 2021 -0500 94 | 95 | add README 96 | 97 | commit 6c600309da41ae3435254d9610876000c6099abb 98 | Author: Oliver Sandoval 99 | Date: Tue Mar 9 16:10:04 2021 -0500 100 | 101 | agregada clase Authentication que valida toda la autenticacion del sistema 102 | 103 | commit 489c4fb61ccfe8030037fb73ae842dc95c8a58af 104 | Author: Oliver Sandoval 105 | Date: Tue Mar 9 02:21:32 2021 -0500 106 | 107 | logout agregado 108 | 109 | commit f97270c91b7fad97bf1009ef4d5d90e142bc2e3f 110 | Author: Oliver Sandoval 111 | Date: Tue Mar 9 02:02:31 2021 -0500 112 | 113 | login con authtoken + sesiones + usuarios agregados 114 | 115 | commit 339ea59ba69023ff2752a0740deeaef70118f273 116 | Author: Oliver Sandoval 117 | Date: Mon Mar 1 03:08:49 2021 -0500 118 | 119 | autodocumentacion de endpoints de API con Swagger implementada 120 | 121 | commit 2461612ed2b983a27517db9aae2ee0b9ea4ee1f7 122 | Author: Oliver Sandoval 123 | Date: Tue Feb 23 08:06:34 2021 -0500 124 | 125 | añadido routers y viewsets para products 126 | 127 | commit 0004700f9515cd897e386afcc2c3607720a2029b 128 | Author: Oliver Sandoval 129 | Date: Fri Feb 19 01:18:58 2021 -0500 130 | 131 | retrieve_update_destroy_api_view explicado e implementado en productos 132 | 133 | commit b2eed12934d497d6b7a54d94e15c7a8f323256cc 134 | Author: Oliver Sandoval 135 | Date: Mon Feb 8 01:49:04 2021 -0500 136 | 137 | implementado ListCreateApiView en product_viewset 138 | 139 | commit a24f35ef33c88bec76afe84253cfe786291b30d3 140 | Author: Oliver Sandoval 141 | Date: Sat Jan 9 02:02:59 2021 -0500 142 | 143 | updateapiview en modelo Producto 144 | 145 | commit 1a4c2fdb2b8e1c4171200168f9378c4a9e958fc4 146 | Author: Oliver Sandoval 147 | Date: Sun Jan 3 23:51:18 2021 -0500 148 | 149 | encriptar contraseña en serializadores 150 | 151 | commit d6e5a679778db27d02b5f6011d25c56976c891f6 152 | Author: Oliver Sandoval 153 | Date: Sun Jan 3 23:00:35 2021 -0500 154 | 155 | to_representation agregada y explicada 156 | 157 | commit 9c35e5d5d46f4a0ed5b92d314255a1e84f9f071a 158 | Author: Oliver Sandoval 159 | Date: Mon Dec 28 00:57:15 2020 -0500 160 | 161 | metodo update de un serializer y metodo save de un serializer 162 | 163 | commit 386f37f1a1c7e44e8744f968145392f9da71e2b1 164 | Author: Oliver Sandoval 165 | Date: Mon Dec 28 00:26:24 2020 -0500 166 | 167 | metodo create en un serializer 168 | 169 | commit ddfc0fbca9c5184ec6fdc9a6934a07836b9ba19c 170 | Author: Oliver Sandoval 171 | Date: Sat Dec 19 02:03:28 2020 -0500 172 | 173 | validaciones en serializadores 174 | 175 | commit 949d1c263b8b061f46697d3558a39dfc3bf4126f 176 | Author: Oliver Sandoval 177 | Date: Tue Dec 15 01:17:21 2020 -0500 178 | 179 | agregada funcion de detalle de usuario, corrección en codigo para optimizarlo y refactorizarlo, hacer entendible el codigo 180 | 181 | commit 24ee390ad874e1d57e0d3f950b0ea739c923824d 182 | Author: Oliver Sandoval 183 | Date: Fri Nov 27 01:41:53 2020 -0500 184 | 185 | commit hasta 5 video de curso de Django Rest Framework, registro con api_view utilizando serializador 186 | -------------------------------------------------------------------------------- /apps/products/migrations/0003_historicalcategoryproduct_historicalindicator_historicalmeasureunit_historicalproduct.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.7 on 2021-11-04 23:42 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | import simple_history.models 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 13 | ('products', '0002_auto_20211104_2341'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='HistoricalProduct', 19 | fields=[ 20 | ('id', models.IntegerField(blank=True, db_index=True)), 21 | ('state', models.BooleanField(default=True, verbose_name='Estado')), 22 | ('created_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Creación')), 23 | ('modified_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Modificación')), 24 | ('deleted_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Eliminación')), 25 | ('name', models.CharField(db_index=True, max_length=150, verbose_name='Nombre de Producto')), 26 | ('description', models.TextField(verbose_name='Descripción de Producto')), 27 | ('image', models.TextField(blank=True, max_length=100, null=True, verbose_name='Imagen del Producto')), 28 | ('history_id', models.AutoField(primary_key=True, serialize=False)), 29 | ('history_date', models.DateTimeField()), 30 | ('history_change_reason', models.CharField(max_length=100, null=True)), 31 | ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), 32 | ('category_product', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='products.categoryproduct', verbose_name='Categoria de Producto')), 33 | ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), 34 | ('measure_unit', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='products.measureunit', verbose_name='Unidad de Medida')), 35 | ], 36 | options={ 37 | 'verbose_name': 'historical Producto', 38 | 'ordering': ('-history_date', '-history_id'), 39 | 'get_latest_by': 'history_date', 40 | }, 41 | bases=(simple_history.models.HistoricalChanges, models.Model), 42 | ), 43 | migrations.CreateModel( 44 | name='HistoricalMeasureUnit', 45 | fields=[ 46 | ('id', models.IntegerField(blank=True, db_index=True)), 47 | ('state', models.BooleanField(default=True, verbose_name='Estado')), 48 | ('created_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Creación')), 49 | ('modified_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Modificación')), 50 | ('deleted_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Eliminación')), 51 | ('description', models.CharField(db_index=True, max_length=50, verbose_name='Descripción')), 52 | ('history_id', models.AutoField(primary_key=True, serialize=False)), 53 | ('history_date', models.DateTimeField()), 54 | ('history_change_reason', models.CharField(max_length=100, null=True)), 55 | ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), 56 | ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), 57 | ], 58 | options={ 59 | 'verbose_name': 'historical Unidad de Medida', 60 | 'ordering': ('-history_date', '-history_id'), 61 | 'get_latest_by': 'history_date', 62 | }, 63 | bases=(simple_history.models.HistoricalChanges, models.Model), 64 | ), 65 | migrations.CreateModel( 66 | name='HistoricalIndicator', 67 | fields=[ 68 | ('id', models.IntegerField(blank=True, db_index=True)), 69 | ('state', models.BooleanField(default=True, verbose_name='Estado')), 70 | ('created_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Creación')), 71 | ('modified_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Modificación')), 72 | ('deleted_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Eliminación')), 73 | ('descount_value', models.PositiveSmallIntegerField(default=0)), 74 | ('history_id', models.AutoField(primary_key=True, serialize=False)), 75 | ('history_date', models.DateTimeField()), 76 | ('history_change_reason', models.CharField(max_length=100, null=True)), 77 | ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), 78 | ('category_product', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='products.categoryproduct', verbose_name='Indicador de Oferta')), 79 | ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), 80 | ], 81 | options={ 82 | 'verbose_name': 'historical Indicador de Oferta', 83 | 'ordering': ('-history_date', '-history_id'), 84 | 'get_latest_by': 'history_date', 85 | }, 86 | bases=(simple_history.models.HistoricalChanges, models.Model), 87 | ), 88 | migrations.CreateModel( 89 | name='HistoricalCategoryProduct', 90 | fields=[ 91 | ('id', models.IntegerField(blank=True, db_index=True)), 92 | ('state', models.BooleanField(default=True, verbose_name='Estado')), 93 | ('created_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Creación')), 94 | ('modified_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Modificación')), 95 | ('deleted_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Eliminación')), 96 | ('description', models.CharField(db_index=True, max_length=50, verbose_name='Descripcion')), 97 | ('history_id', models.AutoField(primary_key=True, serialize=False)), 98 | ('history_date', models.DateTimeField()), 99 | ('history_change_reason', models.CharField(max_length=100, null=True)), 100 | ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), 101 | ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), 102 | ], 103 | options={ 104 | 'verbose_name': 'historical Categoría de Producto', 105 | 'ordering': ('-history_date', '-history_id'), 106 | 'get_latest_by': 'history_date', 107 | }, 108 | bases=(simple_history.models.HistoricalChanges, models.Model), 109 | ), 110 | ] 111 | -------------------------------------------------------------------------------- /apps/products/api/viewsets/general_views.py: -------------------------------------------------------------------------------- 1 | from rest_framework import viewsets 2 | from rest_framework import status 3 | from rest_framework.decorators import action 4 | from rest_framework.response import Response 5 | 6 | from apps.base.api import GeneralListApiView 7 | from apps.products.models import MeasureUnit, CategoryProduct 8 | from apps.products.api.serializers.general_serializers import ( 9 | MeasureUnitSerializer, IndicatorSerializer, CategoryProductSerializer, 10 | IndicatorUpdateSerializer 11 | ) 12 | 13 | class MeasureUnitViewSet(viewsets.GenericViewSet): 14 | model = MeasureUnit 15 | serializer_class = MeasureUnitSerializer 16 | 17 | def get_queryset(self): 18 | return self.get_serializer().Meta.model.objects.filter(state=True) 19 | 20 | def get_object(self): 21 | return self.get_serializer().Meta.model.objects.filter(id=self.kwargs['pk'], state=True) 22 | 23 | @action(detail=False, methods=['get']) 24 | def get_measure_units(self, request): 25 | data = MeasureUnit.objects.filter(state=True) 26 | data = MeasureUnitSerializer(data, many=True) 27 | return Response(data.data) 28 | 29 | def list(self, request): 30 | data = self.get_queryset() 31 | data = self.get_serializer(data, many=True) 32 | data = { 33 | "total": self.get_queryset().count(), 34 | "totalNotFiltered": self.get_queryset().count(), 35 | "rows": data.data 36 | } 37 | return Response(data) 38 | 39 | def create(self, request): 40 | serializer = self.serializer_class(data=request.data) 41 | if serializer.is_valid(): 42 | serializer.save() 43 | return Response({'message':'Unidad de Medida registrada correctamente!'}, status=status.HTTP_201_CREATED) 44 | return Response({'message':'', 'error':serializer.errors}, status=status.HTTP_400_BAD_REQUEST) 45 | 46 | def retrieve(self, request, pk=None): 47 | if self.get_object().exists(): 48 | data = self.get_object().get() 49 | data = self.get_serializer(data) 50 | return Response(data.data) 51 | return Response({'message':'', 'error':'Unidad de Medida no encontrada!'}, status=status.HTTP_400_BAD_REQUEST) 52 | 53 | def update(self, request, pk=None): 54 | if self.get_object().exists(): 55 | serializer = self.serializer_class(instance=self.get_object().get(), data=request.data) 56 | if serializer.is_valid(): 57 | serializer.save() 58 | return Response({'message':'Unidad de Medida actualizada correctamente!'}, status=status.HTTP_200_OK) 59 | return Response({'message':'', 'error':serializer.errors}, status=status.HTTP_400_BAD_REQUEST) 60 | 61 | def destroy(self, request, pk=None): 62 | if self.get_object().exists(): 63 | self.get_object().get().delete() 64 | return Response({'message':'Unidad de Medida eliminada correctamente!'}, status=status.HTTP_200_OK) 65 | return Response({'message':'', 'error':'Unidad de Medida no encontrada!'}, status=status.HTTP_400_BAD_REQUEST) 66 | 67 | class IndicatorViewSet(viewsets.GenericViewSet): 68 | serializer_class = IndicatorSerializer 69 | 70 | def get_queryset(self): 71 | return self.get_serializer().Meta.model.objects.filter(state=True) 72 | 73 | def get_object(self): 74 | return self.get_serializer().Meta.model.objects.filter(id=self.kwargs['pk'], state=True) 75 | 76 | def list(self, request): 77 | data = self.get_queryset() 78 | data = self.get_serializer(data,many=True) 79 | data = { 80 | "total": self.get_queryset().count(), 81 | "totalNotFiltered": self.get_queryset().count(), 82 | "rows": data.data 83 | } 84 | return Response(data) 85 | 86 | def create(self, request): 87 | serializer = self.serializer_class(data=request.data) 88 | if serializer.is_valid(): 89 | serializer.save() 90 | return Response({'message':'Indicador registrado correctamente!'}, status=status.HTTP_201_CREATED) 91 | return Response({'message':'', 'error':serializer.errors}, status=status.HTTP_400_BAD_REQUEST) 92 | 93 | def retrieve(self, request, pk=None): 94 | if self.get_object().exists(): 95 | data = self.get_object().get() 96 | data = IndicatorUpdateSerializer(data) 97 | return Response(data.data) 98 | return Response({'message':'', 'error':'Indicador no encontrado!'}, status=status.HTTP_400_BAD_REQUEST) 99 | 100 | def update(self, request, pk=None): 101 | if self.get_object().exists(): 102 | serializer = self.serializer_class(instance=self.get_object().get(), data=request.data) 103 | if serializer.is_valid(): 104 | serializer.save() 105 | return Response({'message':'Indicador actualizado correctamente!'}, status=status.HTTP_200_OK) 106 | return Response({'message':'', 'error':serializer.errors}, status=status.HTTP_400_BAD_REQUEST) 107 | 108 | def destroy(self, request, pk=None): 109 | if self.get_object().exists(): 110 | self.get_object().get().delete() 111 | return Response({'message':'Indicador eliminado correctamente!'}, status=status.HTTP_200_OK) 112 | return Response({'message':'', 'error':'Indicador no encontrado!'}, status=status.HTTP_400_BAD_REQUEST) 113 | 114 | class CategoryProductViewSet(viewsets.GenericViewSet): 115 | serializer_class = CategoryProductSerializer 116 | 117 | def get_queryset(self): 118 | return self.get_serializer().Meta.model.objects.filter(state=True) 119 | 120 | def get_object(self): 121 | return self.get_serializer().Meta.model.objects.filter(id=self.kwargs['pk'], state=True) 122 | 123 | @action(detail=False, methods=['get']) 124 | def get_categories(self, request): 125 | data = CategoryProduct.objects.filter(state=True) 126 | data = CategoryProductSerializer(data, many=True) 127 | return Response(data.data) 128 | 129 | def list(self, request): 130 | data = self.get_queryset() 131 | data = self.get_serializer(data, many=True) 132 | data = { 133 | "total": self.get_queryset().count(), 134 | "totalNotFiltered": self.get_queryset().count(), 135 | "rows": data.data 136 | } 137 | return Response(data) 138 | 139 | def create(self, request): 140 | serializer = self.serializer_class(data=request.data) 141 | if serializer.is_valid(): 142 | serializer.save() 143 | return Response({'message':'Categoría registrada correctamente!'}, status=status.HTTP_201_CREATED) 144 | return Response({'message':'', 'error':serializer.errors}, status=status.HTTP_400_BAD_REQUEST) 145 | 146 | def retrieve(self, request, pk=None): 147 | if self.get_object().exists(): 148 | data = self.get_object().get() 149 | data = self.get_serializer(data) 150 | return Response(data.data) 151 | return Response({'message':'', 'error':'Categoría no encontrada!'}, status=status.HTTP_400_BAD_REQUEST) 152 | 153 | def update(self, request, pk=None): 154 | if self.get_object().exists(): 155 | serializer = self.serializer_class(instance=self.get_object().get(), data=request.data) 156 | if serializer.is_valid(): 157 | serializer.save() 158 | return Response({'message':'Categoría actualizada correctamente!'}, status=status.HTTP_200_OK) 159 | return Response({'message':'', 'error':serializer.errors}, status=status.HTTP_400_BAD_REQUEST) 160 | 161 | def destroy(self, request, pk=None): 162 | if self.get_object().exists(): 163 | self.get_object().get().delete() 164 | return Response({'message':'Categoría eliminada correctamente!'}, status=status.HTTP_200_OK) 165 | return Response({'message':'', 'error':'Categoría no encontrada!'}, status=status.HTTP_400_BAD_REQUEST) 166 | -------------------------------------------------------------------------------- /apps/products/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.7 on 2021-05-29 00:24 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | import simple_history.models 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 15 | ] 16 | 17 | operations = [ 18 | migrations.CreateModel( 19 | name='CategoryProduct', 20 | fields=[ 21 | ('id', models.AutoField(primary_key=True, serialize=False)), 22 | ('state', models.BooleanField(default=True, verbose_name='Estado')), 23 | ('created_date', models.DateField(auto_now_add=True, verbose_name='Fecha de Creación')), 24 | ('modified_date', models.DateField(auto_now=True, verbose_name='Fecha de Modificación')), 25 | ('deleted_date', models.DateField(auto_now=True, verbose_name='Fecha de Eliminación')), 26 | ('description', models.CharField(max_length=50, unique=True, verbose_name='Descripcion')), 27 | ], 28 | options={ 29 | 'verbose_name': 'Categoría de Producto', 30 | 'verbose_name_plural': 'Categorías de Productos', 31 | }, 32 | ), 33 | migrations.CreateModel( 34 | name='MeasureUnit', 35 | fields=[ 36 | ('id', models.AutoField(primary_key=True, serialize=False)), 37 | ('state', models.BooleanField(default=True, verbose_name='Estado')), 38 | ('created_date', models.DateField(auto_now_add=True, verbose_name='Fecha de Creación')), 39 | ('modified_date', models.DateField(auto_now=True, verbose_name='Fecha de Modificación')), 40 | ('deleted_date', models.DateField(auto_now=True, verbose_name='Fecha de Eliminación')), 41 | ('description', models.CharField(max_length=50, unique=True, verbose_name='Descripción')), 42 | ], 43 | options={ 44 | 'verbose_name': 'Unidad de Medida', 45 | 'verbose_name_plural': 'Unidades de Medidas', 46 | }, 47 | ), 48 | migrations.CreateModel( 49 | name='Product', 50 | fields=[ 51 | ('id', models.AutoField(primary_key=True, serialize=False)), 52 | ('state', models.BooleanField(default=True, verbose_name='Estado')), 53 | ('created_date', models.DateField(auto_now_add=True, verbose_name='Fecha de Creación')), 54 | ('modified_date', models.DateField(auto_now=True, verbose_name='Fecha de Modificación')), 55 | ('deleted_date', models.DateField(auto_now=True, verbose_name='Fecha de Eliminación')), 56 | ('name', models.CharField(max_length=150, unique=True, verbose_name='Nombre de Producto')), 57 | ('description', models.TextField(verbose_name='Descripción de Producto')), 58 | ('image', models.ImageField(blank=True, null=True, upload_to='products/', verbose_name='Imagen del Producto')), 59 | ('category_product', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='products.categoryproduct', verbose_name='Categoria de Producto')), 60 | ('measure_unit', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='products.measureunit', verbose_name='Unidad de Medida')), 61 | ], 62 | options={ 63 | 'verbose_name': 'Producto', 64 | 'verbose_name_plural': 'Productos', 65 | }, 66 | ), 67 | migrations.CreateModel( 68 | name='Indicator', 69 | fields=[ 70 | ('id', models.AutoField(primary_key=True, serialize=False)), 71 | ('state', models.BooleanField(default=True, verbose_name='Estado')), 72 | ('created_date', models.DateField(auto_now_add=True, verbose_name='Fecha de Creación')), 73 | ('modified_date', models.DateField(auto_now=True, verbose_name='Fecha de Modificación')), 74 | ('deleted_date', models.DateField(auto_now=True, verbose_name='Fecha de Eliminación')), 75 | ('descount_value', models.PositiveSmallIntegerField(default=0)), 76 | ('category_product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='products.categoryproduct', verbose_name='Indicador de Oferta')), 77 | ], 78 | options={ 79 | 'verbose_name': 'Indicador de Oferta', 80 | 'verbose_name_plural': 'Indicadores de Ofertas', 81 | }, 82 | ), 83 | migrations.CreateModel( 84 | name='HistoricalProduct', 85 | fields=[ 86 | ('id', models.IntegerField(blank=True, db_index=True)), 87 | ('state', models.BooleanField(default=True, verbose_name='Estado')), 88 | ('created_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Creación')), 89 | ('modified_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Modificación')), 90 | ('deleted_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Eliminación')), 91 | ('name', models.CharField(db_index=True, max_length=150, verbose_name='Nombre de Producto')), 92 | ('description', models.TextField(verbose_name='Descripción de Producto')), 93 | ('image', models.TextField(blank=True, max_length=100, null=True, verbose_name='Imagen del Producto')), 94 | ('history_id', models.AutoField(primary_key=True, serialize=False)), 95 | ('history_date', models.DateTimeField()), 96 | ('history_change_reason', models.CharField(max_length=100, null=True)), 97 | ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), 98 | ('category_product', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='products.categoryproduct', verbose_name='Categoria de Producto')), 99 | ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), 100 | ('measure_unit', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='products.measureunit', verbose_name='Unidad de Medida')), 101 | ], 102 | options={ 103 | 'verbose_name': 'historical Producto', 104 | 'ordering': ('-history_date', '-history_id'), 105 | 'get_latest_by': 'history_date', 106 | }, 107 | bases=(simple_history.models.HistoricalChanges, models.Model), 108 | ), 109 | migrations.CreateModel( 110 | name='HistoricalMeasureUnit', 111 | fields=[ 112 | ('id', models.IntegerField(blank=True, db_index=True)), 113 | ('state', models.BooleanField(default=True, verbose_name='Estado')), 114 | ('created_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Creación')), 115 | ('modified_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Modificación')), 116 | ('deleted_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Eliminación')), 117 | ('description', models.CharField(db_index=True, max_length=50, verbose_name='Descripción')), 118 | ('history_id', models.AutoField(primary_key=True, serialize=False)), 119 | ('history_date', models.DateTimeField()), 120 | ('history_change_reason', models.CharField(max_length=100, null=True)), 121 | ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), 122 | ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), 123 | ], 124 | options={ 125 | 'verbose_name': 'historical Unidad de Medida', 126 | 'ordering': ('-history_date', '-history_id'), 127 | 'get_latest_by': 'history_date', 128 | }, 129 | bases=(simple_history.models.HistoricalChanges, models.Model), 130 | ), 131 | migrations.CreateModel( 132 | name='HistoricalIndicator', 133 | fields=[ 134 | ('id', models.IntegerField(blank=True, db_index=True)), 135 | ('state', models.BooleanField(default=True, verbose_name='Estado')), 136 | ('created_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Creación')), 137 | ('modified_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Modificación')), 138 | ('deleted_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Eliminación')), 139 | ('descount_value', models.PositiveSmallIntegerField(default=0)), 140 | ('history_id', models.AutoField(primary_key=True, serialize=False)), 141 | ('history_date', models.DateTimeField()), 142 | ('history_change_reason', models.CharField(max_length=100, null=True)), 143 | ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), 144 | ('category_product', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='products.categoryproduct', verbose_name='Indicador de Oferta')), 145 | ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), 146 | ], 147 | options={ 148 | 'verbose_name': 'historical Indicador de Oferta', 149 | 'ordering': ('-history_date', '-history_id'), 150 | 'get_latest_by': 'history_date', 151 | }, 152 | bases=(simple_history.models.HistoricalChanges, models.Model), 153 | ), 154 | migrations.CreateModel( 155 | name='HistoricalCategoryProduct', 156 | fields=[ 157 | ('id', models.IntegerField(blank=True, db_index=True)), 158 | ('state', models.BooleanField(default=True, verbose_name='Estado')), 159 | ('created_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Creación')), 160 | ('modified_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Modificación')), 161 | ('deleted_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Eliminación')), 162 | ('description', models.CharField(db_index=True, max_length=50, verbose_name='Descripcion')), 163 | ('history_id', models.AutoField(primary_key=True, serialize=False)), 164 | ('history_date', models.DateTimeField()), 165 | ('history_change_reason', models.CharField(max_length=100, null=True)), 166 | ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), 167 | ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), 168 | ], 169 | options={ 170 | 'verbose_name': 'historical Categoría de Producto', 171 | 'ordering': ('-history_date', '-history_id'), 172 | 'get_latest_by': 'history_date', 173 | }, 174 | bases=(simple_history.models.HistoricalChanges, models.Model), 175 | ), 176 | ] 177 | -------------------------------------------------------------------------------- /apps/expense_manager/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.7 on 2021-11-04 23:42 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | import simple_history.models 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | initial = True 12 | 13 | dependencies = [ 14 | ('products', '0003_historicalcategoryproduct_historicalindicator_historicalmeasureunit_historicalproduct'), 15 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 16 | ] 17 | 18 | operations = [ 19 | migrations.CreateModel( 20 | name='ExpenseCategory', 21 | fields=[ 22 | ('id', models.AutoField(primary_key=True, serialize=False)), 23 | ('state', models.BooleanField(default=True, verbose_name='Estado')), 24 | ('created_date', models.DateField(auto_now_add=True, verbose_name='Fecha de Creación')), 25 | ('modified_date', models.DateField(auto_now=True, verbose_name='Fecha de Modificación')), 26 | ('deleted_date', models.DateField(auto_now=True, verbose_name='Fecha de Eliminación')), 27 | ('name', models.CharField(max_length=100, verbose_name='Nombre de Categoría de Gasto')), 28 | ], 29 | options={ 30 | 'verbose_name': 'Categoria de Gasto', 31 | 'verbose_name_plural': 'Categorias de Gastos', 32 | 'ordering': ['id'], 33 | }, 34 | ), 35 | migrations.CreateModel( 36 | name='PaymentType', 37 | fields=[ 38 | ('id', models.AutoField(primary_key=True, serialize=False)), 39 | ('state', models.BooleanField(default=True, verbose_name='Estado')), 40 | ('created_date', models.DateField(auto_now_add=True, verbose_name='Fecha de Creación')), 41 | ('modified_date', models.DateField(auto_now=True, verbose_name='Fecha de Modificación')), 42 | ('deleted_date', models.DateField(auto_now=True, verbose_name='Fecha de Eliminación')), 43 | ('name', models.CharField(max_length=100, verbose_name='Nombre de Medio de Pago')), 44 | ], 45 | options={ 46 | 'verbose_name': 'Medio de Pago', 47 | 'verbose_name_plural': 'Medio de Pagos', 48 | 'ordering': ['id'], 49 | }, 50 | ), 51 | migrations.CreateModel( 52 | name='Provider', 53 | fields=[ 54 | ('id', models.AutoField(primary_key=True, serialize=False)), 55 | ('state', models.BooleanField(default=True, verbose_name='Estado')), 56 | ('created_date', models.DateField(auto_now_add=True, verbose_name='Fecha de Creación')), 57 | ('modified_date', models.DateField(auto_now=True, verbose_name='Fecha de Modificación')), 58 | ('deleted_date', models.DateField(auto_now=True, verbose_name='Fecha de Eliminación')), 59 | ('ruc', models.CharField(max_length=11, unique=True)), 60 | ('business_name', models.CharField(max_length=150, verbose_name='Razón Social')), 61 | ('address', models.CharField(max_length=200)), 62 | ('phone', models.CharField(blank=True, max_length=15, null=True)), 63 | ('email', models.EmailField(max_length=254, null=True)), 64 | ], 65 | options={ 66 | 'verbose_name': 'Proveedor', 67 | 'verbose_name_plural': 'Proveedores', 68 | 'ordering': ['id'], 69 | }, 70 | ), 71 | migrations.CreateModel( 72 | name='Voucher', 73 | fields=[ 74 | ('id', models.AutoField(primary_key=True, serialize=False)), 75 | ('state', models.BooleanField(default=True, verbose_name='Estado')), 76 | ('created_date', models.DateField(auto_now_add=True, verbose_name='Fecha de Creación')), 77 | ('modified_date', models.DateField(auto_now=True, verbose_name='Fecha de Modificación')), 78 | ('deleted_date', models.DateField(auto_now=True, verbose_name='Fecha de Eliminación')), 79 | ('name', models.CharField(max_length=100, verbose_name='Nombre de comprobante de Pago')), 80 | ], 81 | options={ 82 | 'verbose_name': 'Comprobante', 83 | 'verbose_name_plural': 'Comprobantes', 84 | 'ordering': ['id'], 85 | }, 86 | ), 87 | migrations.CreateModel( 88 | name='Merma', 89 | fields=[ 90 | ('id', models.AutoField(primary_key=True, serialize=False)), 91 | ('state', models.BooleanField(default=True, verbose_name='Estado')), 92 | ('created_date', models.DateField(auto_now_add=True, verbose_name='Fecha de Creación')), 93 | ('modified_date', models.DateField(auto_now=True, verbose_name='Fecha de Modificación')), 94 | ('deleted_date', models.DateField(auto_now=True, verbose_name='Fecha de Eliminación')), 95 | ('date', models.DateField(verbose_name='Fecha de emisión de Merma')), 96 | ('quantity', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='Cantidad')), 97 | ('money_loss', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='Dinero perdido')), 98 | ('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='products.product')), 99 | ], 100 | options={ 101 | 'verbose_name': 'Merma', 102 | 'verbose_name_plural': 'Mermas', 103 | 'ordering': ['id'], 104 | }, 105 | ), 106 | migrations.CreateModel( 107 | name='HistoricalVoucher', 108 | fields=[ 109 | ('id', models.IntegerField(blank=True, db_index=True)), 110 | ('state', models.BooleanField(default=True, verbose_name='Estado')), 111 | ('created_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Creación')), 112 | ('modified_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Modificación')), 113 | ('deleted_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Eliminación')), 114 | ('name', models.CharField(max_length=100, verbose_name='Nombre de comprobante de Pago')), 115 | ('history_id', models.AutoField(primary_key=True, serialize=False)), 116 | ('history_date', models.DateTimeField()), 117 | ('history_change_reason', models.CharField(max_length=100, null=True)), 118 | ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), 119 | ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), 120 | ], 121 | options={ 122 | 'verbose_name': 'historical Comprobante', 123 | 'ordering': ('-history_date', '-history_id'), 124 | 'get_latest_by': 'history_date', 125 | }, 126 | bases=(simple_history.models.HistoricalChanges, models.Model), 127 | ), 128 | migrations.CreateModel( 129 | name='HistoricalProvider', 130 | fields=[ 131 | ('id', models.IntegerField(blank=True, db_index=True)), 132 | ('state', models.BooleanField(default=True, verbose_name='Estado')), 133 | ('created_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Creación')), 134 | ('modified_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Modificación')), 135 | ('deleted_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Eliminación')), 136 | ('ruc', models.CharField(db_index=True, max_length=11)), 137 | ('business_name', models.CharField(max_length=150, verbose_name='Razón Social')), 138 | ('address', models.CharField(max_length=200)), 139 | ('phone', models.CharField(blank=True, max_length=15, null=True)), 140 | ('email', models.EmailField(max_length=254, null=True)), 141 | ('history_id', models.AutoField(primary_key=True, serialize=False)), 142 | ('history_date', models.DateTimeField()), 143 | ('history_change_reason', models.CharField(max_length=100, null=True)), 144 | ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), 145 | ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), 146 | ], 147 | options={ 148 | 'verbose_name': 'historical Proveedor', 149 | 'ordering': ('-history_date', '-history_id'), 150 | 'get_latest_by': 'history_date', 151 | }, 152 | bases=(simple_history.models.HistoricalChanges, models.Model), 153 | ), 154 | migrations.CreateModel( 155 | name='HistoricalPaymentType', 156 | fields=[ 157 | ('id', models.IntegerField(blank=True, db_index=True)), 158 | ('state', models.BooleanField(default=True, verbose_name='Estado')), 159 | ('created_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Creación')), 160 | ('modified_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Modificación')), 161 | ('deleted_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Eliminación')), 162 | ('name', models.CharField(max_length=100, verbose_name='Nombre de Medio de Pago')), 163 | ('history_id', models.AutoField(primary_key=True, serialize=False)), 164 | ('history_date', models.DateTimeField()), 165 | ('history_change_reason', models.CharField(max_length=100, null=True)), 166 | ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), 167 | ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), 168 | ], 169 | options={ 170 | 'verbose_name': 'historical Medio de Pago', 171 | 'ordering': ('-history_date', '-history_id'), 172 | 'get_latest_by': 'history_date', 173 | }, 174 | bases=(simple_history.models.HistoricalChanges, models.Model), 175 | ), 176 | migrations.CreateModel( 177 | name='HistoricalMerma', 178 | fields=[ 179 | ('id', models.IntegerField(blank=True, db_index=True)), 180 | ('state', models.BooleanField(default=True, verbose_name='Estado')), 181 | ('created_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Creación')), 182 | ('modified_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Modificación')), 183 | ('deleted_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Eliminación')), 184 | ('date', models.DateField(verbose_name='Fecha de emisión de Merma')), 185 | ('quantity', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='Cantidad')), 186 | ('money_loss', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='Dinero perdido')), 187 | ('history_id', models.AutoField(primary_key=True, serialize=False)), 188 | ('history_date', models.DateTimeField()), 189 | ('history_change_reason', models.CharField(max_length=100, null=True)), 190 | ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), 191 | ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), 192 | ('product', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='products.product')), 193 | ], 194 | options={ 195 | 'verbose_name': 'historical Merma', 196 | 'ordering': ('-history_date', '-history_id'), 197 | 'get_latest_by': 'history_date', 198 | }, 199 | bases=(simple_history.models.HistoricalChanges, models.Model), 200 | ), 201 | migrations.CreateModel( 202 | name='HistoricalExpenseCategory', 203 | fields=[ 204 | ('id', models.IntegerField(blank=True, db_index=True)), 205 | ('state', models.BooleanField(default=True, verbose_name='Estado')), 206 | ('created_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Creación')), 207 | ('modified_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Modificación')), 208 | ('deleted_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Eliminación')), 209 | ('name', models.CharField(max_length=100, verbose_name='Nombre de Categoría de Gasto')), 210 | ('history_id', models.AutoField(primary_key=True, serialize=False)), 211 | ('history_date', models.DateTimeField()), 212 | ('history_change_reason', models.CharField(max_length=100, null=True)), 213 | ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), 214 | ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), 215 | ], 216 | options={ 217 | 'verbose_name': 'historical Categoria de Gasto', 218 | 'ordering': ('-history_date', '-history_id'), 219 | 'get_latest_by': 'history_date', 220 | }, 221 | bases=(simple_history.models.HistoricalChanges, models.Model), 222 | ), 223 | migrations.CreateModel( 224 | name='HistoricalExpense', 225 | fields=[ 226 | ('id', models.IntegerField(blank=True, db_index=True)), 227 | ('state', models.BooleanField(default=True, verbose_name='Estado')), 228 | ('created_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Creación')), 229 | ('modified_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Modificación')), 230 | ('deleted_date', models.DateField(blank=True, editable=False, verbose_name='Fecha de Eliminación')), 231 | ('date', models.DateField(verbose_name='Fecha de emisión de factura')), 232 | ('quantity', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Cantidad')), 233 | ('unit_price', models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='Precio Unitario')), 234 | ('voucher_number', models.CharField(max_length=50, verbose_name='Número de comprobante')), 235 | ('total', models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='Total')), 236 | ('history_id', models.AutoField(primary_key=True, serialize=False)), 237 | ('history_date', models.DateTimeField()), 238 | ('history_change_reason', models.CharField(max_length=100, null=True)), 239 | ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), 240 | ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), 241 | ('payment_type', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='expense_manager.paymenttype')), 242 | ('product', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='products.product')), 243 | ('provider', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='expense_manager.provider')), 244 | ('user', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL)), 245 | ('voucher', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='expense_manager.voucher')), 246 | ], 247 | options={ 248 | 'verbose_name': 'historical Gasto', 249 | 'ordering': ('-history_date', '-history_id'), 250 | 'get_latest_by': 'history_date', 251 | }, 252 | bases=(simple_history.models.HistoricalChanges, models.Model), 253 | ), 254 | migrations.CreateModel( 255 | name='Expense', 256 | fields=[ 257 | ('id', models.AutoField(primary_key=True, serialize=False)), 258 | ('state', models.BooleanField(default=True, verbose_name='Estado')), 259 | ('created_date', models.DateField(auto_now_add=True, verbose_name='Fecha de Creación')), 260 | ('modified_date', models.DateField(auto_now=True, verbose_name='Fecha de Modificación')), 261 | ('deleted_date', models.DateField(auto_now=True, verbose_name='Fecha de Eliminación')), 262 | ('date', models.DateField(verbose_name='Fecha de emisión de factura')), 263 | ('quantity', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Cantidad')), 264 | ('unit_price', models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='Precio Unitario')), 265 | ('voucher_number', models.CharField(max_length=50, verbose_name='Número de comprobante')), 266 | ('total', models.DecimalField(decimal_places=2, default=0, max_digits=10, verbose_name='Total')), 267 | ('payment_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='expense_manager.paymenttype')), 268 | ('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='products.product')), 269 | ('provider', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='expense_manager.provider')), 270 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 271 | ('voucher', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='expense_manager.voucher')), 272 | ], 273 | options={ 274 | 'verbose_name': 'Gasto', 275 | 'verbose_name_plural': 'Gastos', 276 | 'ordering': ['id'], 277 | }, 278 | ), 279 | ] 280 | --------------------------------------------------------------------------------