diff --git a/multigranja/apps/core/static/core/js/main.js b/multigranja/apps/core/static/core/js/main.js
index ad7c8c0..6164005 100644
--- a/multigranja/apps/core/static/core/js/main.js
+++ b/multigranja/apps/core/static/core/js/main.js
@@ -41,4 +41,11 @@ document.addEventListener("DOMContentLoaded", () => {
}
});
});
+});
+
+// Blocks scroll form changing the value on inputs type number
+document.querySelectorAll('input[type="number"]').forEach(input => {
+ input.addEventListener('wheel', function(event) {
+ event.target.blur();
+ });
});
\ No newline at end of file
diff --git a/multigranja/apps/core/templates/layouts/base.html b/multigranja/apps/core/templates/layouts/base.html
index 21ac856..8b88ec7 100644
--- a/multigranja/apps/core/templates/layouts/base.html
+++ b/multigranja/apps/core/templates/layouts/base.html
@@ -11,10 +11,12 @@
-
+
+
+
{% block extra_css %}{% endblock %}
diff --git a/multigranja/apps/entities/admin.py b/multigranja/apps/entities/admin.py
index 6c53895..d4374f7 100644
--- a/multigranja/apps/entities/admin.py
+++ b/multigranja/apps/entities/admin.py
@@ -2,7 +2,7 @@ from django.contrib import admin
from .models.user import User
from .models.farm import Farm, PaddockGroup, Paddock
-from .models.animal import Animal, Batch, BatchAnimal, FeedingRecord
+from .models.animal import Animal, BatchType, Batch, BatchAnimal, FeedingRecord
from .models.rotation import Rotation
admin.site.register([
@@ -15,4 +15,5 @@ admin.site.register([
BatchAnimal,
FeedingRecord,
Rotation,
+ BatchType
])
\ No newline at end of file
diff --git a/multigranja/apps/entities/forms/animal.py b/multigranja/apps/entities/forms/animal.py
index f7c9e93..6216035 100644
--- a/multigranja/apps/entities/forms/animal.py
+++ b/multigranja/apps/entities/forms/animal.py
@@ -1,5 +1,5 @@
from django import forms
-from ..models.animal import Batch, Animal
+from ..models.animal import Batch, Animal, BatchType
class AnimalCreateForm(forms.ModelForm):
class Meta:
@@ -42,4 +42,15 @@ class BatchForm(forms.ModelForm):
'batch_type': 'Tipo',
'animals': 'Animais',
}
-BatchCreateForm = BatchEditForm = BatchForm # Alias to BatchForm since its implementations are the same
\ No newline at end of file
+BatchCreateForm = BatchEditForm = BatchForm # Alias to BatchForm since its implementations are the same
+
+class BatchTypeForm(forms.ModelForm):
+ class Meta:
+ model = BatchType
+ fields = ['name', 'consumption_factor']
+
+ labels = {
+ 'name': 'Nome',
+ 'consumption_factor': 'Fator de consumo',
+ }
+BatchTypeCreateForm = BatchTypeEditForm = BatchTypeForm
\ No newline at end of file
diff --git a/multigranja/apps/entities/migrations/0002_batchtype_alter_batch_batch_type.py b/multigranja/apps/entities/migrations/0002_batchtype_alter_batch_batch_type.py
new file mode 100644
index 0000000..d8e317b
--- /dev/null
+++ b/multigranja/apps/entities/migrations/0002_batchtype_alter_batch_batch_type.py
@@ -0,0 +1,29 @@
+# Generated by Django 6.0.2 on 2026-05-30 03:13
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('entities', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='BatchType',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('is_active', models.BooleanField(default=True)),
+ ('name', models.CharField(max_length=45)),
+ ('consumption_factor', models.DecimalField(decimal_places=2, default=1.0, max_digits=4)),
+ ('farm', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='batch_types', to='entities.farm')),
+ ],
+ ),
+ migrations.AlterField(
+ model_name='batch',
+ name='batch_type',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='batches', to='entities.batchtype'),
+ ),
+ ]
diff --git a/multigranja/apps/entities/migrations/0003_farm_default_consumption_farm_summer_consumption_and_more.py b/multigranja/apps/entities/migrations/0003_farm_default_consumption_farm_summer_consumption_and_more.py
new file mode 100644
index 0000000..346dc94
--- /dev/null
+++ b/multigranja/apps/entities/migrations/0003_farm_default_consumption_farm_summer_consumption_and_more.py
@@ -0,0 +1,28 @@
+# Generated by Django 6.0.2 on 2026-05-30 19:18
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('entities', '0002_batchtype_alter_batch_batch_type'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='farm',
+ name='default_consumption',
+ field=models.DecimalField(decimal_places=2, default=1.0, max_digits=4),
+ ),
+ migrations.AddField(
+ model_name='farm',
+ name='summer_consumption',
+ field=models.DecimalField(decimal_places=2, default=1.0, max_digits=4),
+ ),
+ migrations.AddField(
+ model_name='farm',
+ name='winter_consumption',
+ field=models.DecimalField(decimal_places=2, default=1.0, max_digits=4),
+ ),
+ ]
diff --git a/multigranja/apps/entities/models/animal.py b/multigranja/apps/entities/models/animal.py
index 995fa4c..f3c9b83 100644
--- a/multigranja/apps/entities/models/animal.py
+++ b/multigranja/apps/entities/models/animal.py
@@ -35,6 +35,10 @@ class Animal(models.Model):
return f"{months} meses" # MM months
else:
return "Menos de um mês" # Less than a month
+
+ @property
+ def is_in_batch(self):
+ return self.batchanimal_set.filter(is_active=True).exists()
class Meta:
constraints = [
@@ -45,11 +49,25 @@ class Animal(models.Model):
)
]
+class BatchType(models.Model):
+ is_active = models.BooleanField(default=True)
+ farm = models.ForeignKey('entities.Farm', on_delete=models.CASCADE, related_name='batch_types')
+ name = models.CharField(max_length=45)
+ consumption_factor = models.DecimalField(max_digits=4, decimal_places=2, default=1.00)
+
+ def __str__(self):
+ state = 'Ativo' if self.is_active else 'Inativo'
+ return f"{state} - {self.name}, {self.consumption_factor}"
+
+ @property
+ def consumption_percentage(self):
+ return int((self.consumption_factor - 1) * 100)
+
class Batch(models.Model):
is_active = models.BooleanField(default=True)
identifier = models.CharField(max_length=45)
animal_category = models.CharField(max_length=45, null=True, blank=True)
- batch_type = models.CharField(max_length=45, null=True, blank=True)
+ batch_type = models.ForeignKey(BatchType, on_delete=models.SET_NULL, related_name='batches', null=True, blank=True)
animals = models.ManyToManyField(Animal, through='BatchAnimal', related_name='batches')
def __init__(self, *args, **kwargs):
@@ -93,6 +111,16 @@ class BatchAnimal(models.Model):
def __str__(self):
return f"{self.animal.identifier} in {self.batch.identifier}"
+
+ def save(self, *args, **kwargs):
+ is_active_changed = self.__original_is_active != self.is_active
+ if is_active_changed and self.is_active:
+ raise ValueError(
+ "Reactivating a BatchAnimal is not allowed automatically because it may violate "
+ "the unique active batch constraint for an animal. Review the logic in "
+ "models/animal.py (BatchAnimal.save) before proceeding."
+ )
+ super().save(*args, **kwargs)
class FeedingRecord(models.Model):
animal = models.ForeignKey(Animal, on_delete=models.CASCADE, related_name='feeding_record')
diff --git a/multigranja/apps/entities/models/farm.py b/multigranja/apps/entities/models/farm.py
index ecc7aeb..db53649 100644
--- a/multigranja/apps/entities/models/farm.py
+++ b/multigranja/apps/entities/models/farm.py
@@ -4,9 +4,26 @@ class Farm(models.Model):
is_active = models.BooleanField(default=True)
name = models.CharField(max_length=45)
total_area = models.IntegerField(null=True, blank=True)
+ default_consumption = models.DecimalField(max_digits=4, decimal_places=2, default=1.00)
+ summer_consumption = models.DecimalField(max_digits=4, decimal_places=2, default=1.00)
+ winter_consumption = models.DecimalField(max_digits=4, decimal_places=2, default=1.00)
+
def __str__(self):
return self.name
+
+ @property
+ def animal_count(self):
+ return self.animals.filter(
+ is_active=True
+ ).count()
+
+ @property
+ def paddock_count(self):
+ return self.paddocks.filter(
+ is_active=True
+ ).count()
+
class PaddockGroup(models.Model):
is_active = models.BooleanField(default=True)
diff --git a/multigranja/apps/entities/services/animal.py b/multigranja/apps/entities/services/animal.py
index 10f58d7..f180638 100644
--- a/multigranja/apps/entities/services/animal.py
+++ b/multigranja/apps/entities/services/animal.py
@@ -1,8 +1,8 @@
from django.shortcuts import get_object_or_404
from django.urls import reverse
-from ..models import Animal, Batch
-from ..forms import AnimalCreateForm, AnimalEditForm, BatchForm
+from ..models import Animal, Batch, BatchType
+from ..forms import AnimalCreateForm, AnimalEditForm, BatchForm, BatchTypeCreateForm, BatchTypeEditForm
from entities.models import Farm
# Local utility functions
@@ -15,15 +15,36 @@ def _get_selected_farm(request):
def _get_animal_by_id(request, animal_id):
return get_object_or_404(Animal, pk=animal_id, farm_id=_get_selected_farm_id(request))
-def _get_farms_active_animals(request):
- return Animal.objects.filter(
- farm=_get_selected_farm_id(request),
- is_active=True
- )
+# def _get_farms_active_animals(request):
+# return Animal.objects.filter(
+# farm=_get_selected_farm_id(request),
+# is_active=True
+# )
def _get_batch_by_id(request, batch_id):
return get_object_or_404(Batch, pk=batch_id, is_active=True)
+def _get_farms_batch_types(request):
+ return BatchType.objects.filter(is_active=True, farm_id=_get_selected_farm_id(request))
+
+def _get_no_batch_farms_animals(request):
+ return Animal.objects.filter(
+ farm_id=_get_selected_farm_id(request),
+ is_active=True
+ ).exclude(
+ batchanimal__is_active=True
+ )
+
+def _get_animals_from_batch(batch_id):
+ return Batch.objects.get(
+ id=batch_id
+ ).animals.filter(
+ batchanimal__is_active=True
+ )
+
+def _get_batchtype_by_id(request, batchtype_id):
+ return get_object_or_404(BatchType, pk=batchtype_id, is_active=True, farm=_get_selected_farm(request))
+
# Animal Services
def get_animal_create_context():
form = AnimalCreateForm()
@@ -92,7 +113,8 @@ def delete_animal(request, animal_id):
# Batch Services
def get_batch_create_context(request):
form = BatchForm()
- form.fields['animals'].queryset = _get_farms_active_animals(request)
+ form.fields['animals'].queryset = _get_no_batch_farms_animals(request)
+ form.fields['batch_type'].queryset = _get_farms_batch_types(request)
context = {
'title': 'Novo Lote',
@@ -117,8 +139,12 @@ def create_batch(request):
def get_batch_update_context(request, batch_id):
batch_old = _get_batch_by_id(request, batch_id)
+ animals_querset = _get_no_batch_farms_animals(request) | _get_animals_from_batch(batch_id)
+ animals_querset = animals_querset.distinct()
+
form = BatchForm(instance=batch_old)
- form.fields['animals'].queryset = _get_farms_active_animals(request)
+ form.fields['animals'].queryset = animals_querset
+ form.fields['batch_type'].queryset = _get_farms_batch_types(request)
context = {
'title': f'Editar Lote {batch_old.identifier}',
@@ -163,4 +189,76 @@ def get_batch_delete_context(request, batch_id):
def delete_batch(request, batch_id):
batch = _get_batch_by_id(request, batch_id)
batch.is_active = False
- batch.save()
\ No newline at end of file
+ batch.save()
+
+# Batch Type Services
+def get_batchtype_create_context(request):
+ form = BatchTypeCreateForm()
+
+ context = {
+ 'title': 'Novo Tipo de Lote',
+ 'form': form,
+ 'action_url': reverse('entities:animal:batchtype_create'),
+ 'return_url': reverse('entities:farm:farm_home'),
+ }
+
+ return context
+
+def create_batchtype(request):
+ form = BatchTypeCreateForm(request.POST)
+
+ if form.is_valid():
+ batchtype = form.save(commit=False)
+ batchtype.farm = _get_selected_farm(request)
+ batchtype.save()
+
+ return True
+
+ return False
+
+def get_batchtype_update_context(request, batchtype_id):
+ batchtype = _get_batchtype_by_id(request, batchtype_id)
+ form = BatchTypeEditForm(instance=batchtype)
+
+ context = {}
+ context['title'] = 'Editar tipo de lote'
+ context['action_url'] = reverse(
+ 'entities:animal:batchtype_update',
+ kwargs={'batchtype_id': batchtype.pk}
+ )
+ context['return_url'] = reverse('entities:farm:farm_home')
+ context['form'] = form
+
+ return context
+
+def update_batchtype(request, batchtype_id):
+ batchtype = _get_batchtype_by_id(request, batchtype_id)
+ form = BatchTypeEditForm(request.POST, instance=batchtype)
+
+ if form.is_valid():
+ form.save()
+ return True
+
+ return False
+
+def get_batchtype_delete_context(request, batchtype_id):
+ batchtype = _get_batchtype_by_id(request, batchtype_id)
+ context = {}
+ context['description'] = f"Excluir tipo de lote {batchtype.name}?"
+ context['positive_url'] = reverse('entities:animal:batchtype_delete', kwargs={'batchtype_id': batchtype_id})
+ context['negative_url'] = reverse('entities:farm:farm_home')
+
+ return context
+
+def delete_batchtype(request, batchtype_id):
+ batchtype = _get_batchtype_by_id(request, batchtype_id)
+
+ Batch.objects.filter(
+ is_active=True,
+ batch_type=batchtype
+ ).update(
+ batch_type=None
+ )
+
+ batchtype.is_active = False
+ batchtype.save()
\ No newline at end of file
diff --git a/multigranja/apps/entities/services/farm.py b/multigranja/apps/entities/services/farm.py
index f22008f..5ad9fcd 100644
--- a/multigranja/apps/entities/services/farm.py
+++ b/multigranja/apps/entities/services/farm.py
@@ -1,7 +1,9 @@
from django.shortcuts import get_object_or_404
from django.urls import reverse
+from django.db.models import Count, Q
+from decimal import Decimal
-from ..models import Farm, Paddock, PaddockGroup
+from ..models import Farm, Paddock, PaddockGroup, BatchType, User
from ..forms import FarmCreateForm, FarmEditForm, PaddockCreateForm, PaddockEditForm, PaddockGroupCreateForm, PaddockGroupEditForm
@@ -43,20 +45,36 @@ def _get_paddock_group_by_id(request, paddock_group_id):
is_active=True
)
+def _get_farms_batch_types(request):
+ return BatchType.objects.filter(is_active=True, farm=_get_selected_farm(request))
# Farm Services
def get_farm_home_context(request):
authenticated_user = request.user
+ selected_farm = _get_selected_farm(request)
+
+ farms_query = authenticated_user.farms.filter(is_active=True)
+ farms_with_counts = farms_query.annotate(
+ total_animals=Count(
+ 'animals',
+ filter=Q(animals__is_active=True),
+ distinct=True
+ ),
+ total_paddocks=Count(
+ 'paddocks',
+ filter=Q(paddocks__is_active=True),
+ distinct=True
+ )
+ )
+ allowed_users = selected_farm.users.exclude(pk=authenticated_user.pk)
+ batch_types = _get_farms_batch_types(request)
context = {}
- context['title'] = 'Fazenda'
-
- farms = Farm.objects.filter(
- users=authenticated_user,
- is_active=True
- )
-
- context['farms'] = farms
+ context['title'] = selected_farm.name
+ context['selected_farm'] = selected_farm
+ context['farms'] = farms_with_counts
+ context['allowed_users'] = allowed_users
+ context['batch_types'] = batch_types
return context
@@ -67,7 +85,7 @@ def get_farm_create_context():
context['title'] = 'Nova fazenda'
context['form'] = form
context['action_url'] = reverse('entities:farm:farm_create')
- context['return_url'] = reverse('entities:farm:farm_list')
+ context['return_url'] = reverse('entities:farm:farm_home')
return context
@@ -92,7 +110,7 @@ def get_update_farm_context(request, farm_id):
'entities:farm:farm_update',
kwargs={'farm_id': farm.pk}
)
- context['return_url'] = reverse('entities:farm:farm_list')
+ context['return_url'] = reverse('entities:farm:farm_home')
context['form'] = form
return context
@@ -116,7 +134,7 @@ def get_farm_delete_context(request, farm_id):
'entities:farm:farm_delete',
kwargs={'farm_id': farm_id}
)
- context['negative_url'] = reverse('entities:farm:farm_list')
+ context['negative_url'] = reverse('entities:farm:farm_home')
return context
@@ -125,6 +143,17 @@ def delete_farm(request, farm_id):
farm.is_active = False
farm.save()
+def update_consumption(request):
+ default_consumption = Decimal(request.POST.get('default-consume'))
+ summer_consumption = Decimal(request.POST.get('summer-consume'))
+ winter_consumption = Decimal(request.POST.get('winter-consume'))
+
+ selected_farm = _get_selected_farm(request)
+ selected_farm.default_consumption = default_consumption
+ selected_farm.summer_consumption = summer_consumption
+ selected_farm.winter_consumption = winter_consumption
+
+ selected_farm.save()
# Paddock Services
def get_paddock_create_context():
@@ -270,4 +299,41 @@ def get_paddock_group_delete_context(request, paddock_group_id):
def delete_paddock_group(request, paddock_group_id):
paddock_group = _get_paddock_group_by_id(request, paddock_group_id)
paddock_group.is_active = False
- paddock_group.save()
\ No newline at end of file
+ paddock_group.save()
+
+# User Authorize Services
+def authorize_user(request, user_id):
+ farm = _get_selected_farm(request)
+ user = get_object_or_404(User, id=user_id, is_active=True)
+ user.farms.add(farm)
+
+def get_deauthorize_user_context(request, user_id):
+ user = get_object_or_404(User, id=user_id, is_active=True)
+
+ context = {}
+ context['description'] = f"Remover acesso de {user.short_name}?"
+ context['positive_url'] = reverse(
+ 'entities:farm:farm_user_deauthorize',
+ kwargs={'user_id': user_id}
+ )
+ context['negative_url'] = reverse('entities:farm:farm_home')
+
+ return context
+
+def deauthorize_user(request, user_id):
+ farm = _get_selected_farm(request)
+ user = get_object_or_404(User, id=user_id, is_active=True)
+ user.farms.remove(farm)
+
+def get_user_card_context(request):
+ context = {}
+ informed_username = request.GET.get('username')
+
+ found_user = User.objects.filter(login=informed_username).first()
+
+ if found_user.farms.contains(_get_selected_farm(request)):
+ context['already_allowed'] = 'True'
+ else:
+ context['found_user'] = found_user
+
+ return context
\ No newline at end of file
diff --git a/multigranja/apps/entities/static/farm/css/user_authorize.css b/multigranja/apps/entities/static/farm/css/user_authorize.css
new file mode 100644
index 0000000..4f9507a
--- /dev/null
+++ b/multigranja/apps/entities/static/farm/css/user_authorize.css
@@ -0,0 +1,120 @@
+.user-authorize-title {
+ margin-bottom: 20px;
+ font-size: 28px;
+ font-weight: 600;
+}
+
+.user-search {
+ background: #fff;
+ border: 1px solid #e9ecef;
+ border-radius: 12px;
+ padding: 20px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+}
+
+.user-search label {
+ display: block;
+ margin-bottom: 8px;
+ font-weight: 500;
+}
+
+.user-search input {
+ width: 100%;
+ padding: 10px 12px;
+ border: 1px solid #dee2e6;
+ border-radius: 8px;
+ margin-bottom: 12px;
+ transition: border-color 0.2s ease;
+}
+
+.user-search input:focus {
+ outline: none;
+ border-color: #198754;
+}
+
+.user-search button {
+ width: 100%;
+ border: none;
+ border-radius: 8px;
+ padding: 10px;
+ cursor: pointer;
+ font-weight: 500;
+ color: white;
+ background: #198754;
+ transition: 0.2s ease;
+}
+
+.user-search button:hover {
+ background: #157347;
+}
+
+.user-card-container {
+ margin-top: 20px;
+ min-height: 150px;
+ width: 350px;
+}
+
+.user-card {
+ border: 1px solid #e9ecef;
+ border-radius: 10px;
+ padding: 16px;
+ background: #fafafa;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.user-card .name {
+ margin: 0;
+ font-size: 16px;
+ font-weight: 600;
+}
+
+.user-card .name i {
+ margin-right: 6px;
+ color: #6c757d;
+}
+
+.user-card .alert {
+ margin: 0;
+ font-size: 13px;
+ color: #6c757d;
+}
+
+.user-card button {
+ align-self: flex-end;
+ text-decoration: none;
+ background: #198754;
+ color: white;
+ padding: 8px 14px;
+ border-radius: 8px;
+ font-size: 14px;
+ font-weight: 500;
+ transition: 0.2s ease;
+}
+
+.user-card button:hover {
+ background: #157347;
+}
+
+.failed-user-find {
+ text-align: center;
+ padding: 15px;
+ color: #dc3545;
+ font-size: 14px;
+}
+
+.failed-user-find p {
+ margin: 0;
+}
+
+.already-allowed-user {
+ text-align: center;
+ padding: 15px;
+ color: #198754;
+ font-size: 14px;
+}
+
+.already-allowed-user p {
+ margin: 0;
+}
\ No newline at end of file
diff --git a/multigranja/apps/entities/static/rotation/js/rotation_list.js b/multigranja/apps/entities/static/rotation/js/rotation_list.js
index a4247b2..44c4400 100644
--- a/multigranja/apps/entities/static/rotation/js/rotation_list.js
+++ b/multigranja/apps/entities/static/rotation/js/rotation_list.js
@@ -1,13 +1,16 @@
document.addEventListener("DOMContentLoaded", () => {
const container = document.querySelector(".open-rotations-container");
-
+
if (!container) return;
-
+
let targetScroll = container.scrollLeft;
let currentScroll = container.scrollLeft;
let isAnimating = false;
-
+
container.addEventListener("wheel", (event) => {
+ let isMobile = window.innerWidth <= 768;
+ if (isMobile) return;
+
event.preventDefault();
const maxScroll =
diff --git a/multigranja/apps/entities/templates/batch/batchtype_create.html b/multigranja/apps/entities/templates/batch/batchtype_create.html
new file mode 100644
index 0000000..af837e3
--- /dev/null
+++ b/multigranja/apps/entities/templates/batch/batchtype_create.html
@@ -0,0 +1,11 @@
+{% extends 'layouts/base_modal.html' %}
+{% load static %}
+
+{% block extra_css %}
+
+{% endblock %}
+
+{% block content %}
+ NOVO TIPO DE LOTE
+ {% include 'components/form.html' %}
+{% endblock %}
\ No newline at end of file
diff --git a/multigranja/apps/entities/templates/batch/batchtype_update.html b/multigranja/apps/entities/templates/batch/batchtype_update.html
new file mode 100644
index 0000000..2137f73
--- /dev/null
+++ b/multigranja/apps/entities/templates/batch/batchtype_update.html
@@ -0,0 +1,11 @@
+{% extends 'layouts/base_modal.html' %}
+{% load static %}
+
+{% block extra_css %}
+
+{% endblock %}
+
+{% block content %}
+ EDITAR TIPO DE LOTE
+ {% include 'components/form.html' %}
+{% endblock %}
\ No newline at end of file
diff --git a/multigranja/apps/entities/templates/farm/farm_home.html b/multigranja/apps/entities/templates/farm/farm_home.html
index cbde976..66e6c6e 100644
--- a/multigranja/apps/entities/templates/farm/farm_home.html
+++ b/multigranja/apps/entities/templates/farm/farm_home.html
@@ -1,46 +1,105 @@
{% extends 'layouts/base.html' %}
{% load static %}
+{% load l10n %}
{% block extra_css %}
-
+ {{ block.super }}
+
{% endblock %}
{% block content %}
-