diff options
-rw-r--r-- | archaeological_finds/fixtures/initial_data-fr.json | 32 | ||||
-rw-r--r-- | archaeological_finds/forms.py | 24 | ||||
-rw-r--r-- | archaeological_finds/forms_treatments.py | 59 | ||||
-rw-r--r-- | archaeological_finds/migrations/0044_auto_20181201_1854.py | 36 | ||||
-rw-r--r-- | archaeological_finds/migrations/0045_migrate_current_container_to_ref_container.py | 40 | ||||
-rw-r--r-- | archaeological_finds/models_finds.py | 157 | ||||
-rw-r--r-- | archaeological_finds/models_treatments.py | 40 | ||||
-rw-r--r-- | archaeological_finds/templates/ishtar/forms/qa_find_treatment.html | 14 | ||||
-rw-r--r-- | archaeological_finds/templates/ishtar/sheet_find.html | 16 | ||||
m--------- | drassm_app | 0 | ||||
-rw-r--r-- | ishtar_common/templates/blocks/bs_field_snippet.html | 2 |
11 files changed, 363 insertions, 57 deletions
diff --git a/archaeological_finds/fixtures/initial_data-fr.json b/archaeological_finds/fixtures/initial_data-fr.json index a346da9ab..3125ab8da 100644 --- a/archaeological_finds/fixtures/initial_data-fr.json +++ b/archaeological_finds/fixtures/initial_data-fr.json @@ -890,6 +890,38 @@ } }, { + "model": "archaeological_finds.treatmenttype", + "fields": { + "label": "Pr\u00eat", + "txt_idx": "loan", + "comment": "Un pr\u00eat est un changement temporaire de contenant pour du mobilier.", + "available": true, + "parent": null, + "order": 10, + "virtual": false, + "destructive": false, + "create_new_find": false, + "upstream_is_many": false, + "downstream_is_many": false + } +}, +{ + "model": "archaeological_finds.treatmenttype", + "fields": { + "label": "Retour de pr\u00eat", + "txt_idx": "loan-return", + "comment": "Retour de mobilier dans son contenant de r\u00e9f\u00e9rence.", + "available": true, + "parent": null, + "order": 10, + "virtual": false, + "destructive": false, + "create_new_find": false, + "upstream_is_many": false, + "downstream_is_many": false + } +}, +{ "model": "archaeological_finds.treatmentstate", "fields": { "label": "Pr\u00e9vu", diff --git a/archaeological_finds/forms.py b/archaeological_finds/forms.py index e896b310d..e42259bad 100644 --- a/archaeological_finds/forms.py +++ b/archaeological_finds/forms.py @@ -930,20 +930,36 @@ class FindSelect(HistorySelect): class FindSelectWarehouseModule(FindSelect): + container_ref__location = forms.IntegerField( + label=_(u"Reference container - Warehouse (location)"), + widget=widgets.JQueryAutoComplete( + reverse_lazy('autocomplete-warehouse'), + associated_model=Warehouse), + validators=[valid_id(Warehouse)]) + container_ref__responsible = forms.IntegerField( + label=_(u"Reference container - Warehouse (responsible)"), + widget=widgets.JQueryAutoComplete( + reverse_lazy('autocomplete-warehouse'), + associated_model=Warehouse), + validators=[valid_id(Warehouse)]) + container_ref__index = forms.IntegerField( + label=_(u"Reference container ID")) + container_ref__reference = forms.CharField( + label=_(u"Reference container ref.")) container__location = forms.IntegerField( - label=_(u"Warehouse (location)"), + label=_(u"Current container - Warehouse (location)"), widget=widgets.JQueryAutoComplete( reverse_lazy('autocomplete-warehouse'), associated_model=Warehouse), validators=[valid_id(Warehouse)]) container__responsible = forms.IntegerField( - label=_(u"Warehouse (responsible)"), + label=_(u"Current container - Warehouse (responsible)"), widget=widgets.JQueryAutoComplete( reverse_lazy('autocomplete-warehouse'), associated_model=Warehouse), validators=[valid_id(Warehouse)]) - container__index = forms.IntegerField(label=_(u"Container ID")) - container__reference = forms.CharField(label=_(u"Container ref.")) + container__index = forms.IntegerField(label=_(u"Current container ID")) + container__reference = forms.CharField(label=_(u"Current container ref.")) class FindFormSelection(CustomFormSearch): diff --git a/archaeological_finds/forms_treatments.py b/archaeological_finds/forms_treatments.py index 4f120adbf..a0ac1a7c6 100644 --- a/archaeological_finds/forms_treatments.py +++ b/archaeological_finds/forms_treatments.py @@ -23,7 +23,6 @@ from collections import OrderedDict from django import forms from django.core import validators -from django.forms.formsets import formset_factory from django.utils.translation import ugettext_lazy as _ from archaeological_finds import models @@ -132,7 +131,7 @@ class BaseTreatmentForm(CustomForm, ManageOldType): end_date = forms.DateField(label=_(u"Closing date"), required=False, widget=DatePicker) container = forms.IntegerField( - label=_(u"Container (relevant for packaging)"), + label=_(u"Container (relevant for packaging and loan)"), widget=widgets.JQueryAutoComplete( reverse_lazy('autocomplete-container'), associated_model=Container, new=True), @@ -194,39 +193,45 @@ class BaseTreatmentForm(CustomForm, ManageOldType): def clean(self, *args, **kwargs): data = self.cleaned_data + loan = models.TreatmentType.get_cache('loan') + loan_return = models.TreatmentType.get_cache('loan-return') packaging = models.TreatmentType.get_cache('packaging') if not packaging: logger.warning("No 'packaging' treatment type defined") return + treatment_types = data.get('treatment_type', []) + + if (str(packaging.pk) in treatment_types + or str(loan.pk) in treatment_types) and \ + str(loan_return.pk) in treatment_types: + raise forms.ValidationError( + _(u"\"Loan return\" is not compatible with \"loan\" or " + u"\"packaging\" treatments.")) + + if str(packaging.pk) in treatment_types \ + and str(loan.pk) in treatment_types: + raise forms.ValidationError( + _(u"Packaging (concerning the reference location) and loan " + u"(concerning a temporary location) cannot be in the same " + u"treatment.")) if data.get('container', None) \ - and str(packaging.pk) not in data.get('treatment_type', []): + and str(packaging.pk) not in treatment_types\ + and str(loan.pk) not in treatment_types: raise forms.ValidationError( _(u"The container field is attached to the treatment. If " - u"no packaging treatment is done it is not relevant.")) - if not data.get('container', None) \ - and str(packaging.pk) in data.get('treatment_type', []): + u"no packaging or no loan treatment is done it is not " + u"relevant.")) + if not data.get('container', None) and ( + str(packaging.pk) in treatment_types or + str(loan.pk) in treatment_types): raise forms.ValidationError( - _(u"If a packaging treatment is done, the container field " + _(u"If a packaging/loan treatment is done, the container field " u"must be filled.")) if not data.get('person', None) and not data.get('organization', None): raise forms.ValidationError( _(u"A responsible or an organization must be defined.")) return data - # TODO - """ - for treatment_type in self.cleaned_data.get('treatment_type', []): - try: - treatment = models.TreatmentType.objects.get( - pk=treatment_type, available=True) - except models.TreatmentType.DoesNotExist: - raise forms.ValidationError(_(u"This treatment type is not " - u"available.")) - if treatment.upstream_is_many and \ - not self.cleaned_data.get('basket'): - raise forms.ValidationError(_(u"This treatment needs a " - u"basket.")) - """ class N1TreatmentForm(BaseTreatmentForm): @@ -314,6 +319,11 @@ class QAFindTreatmentForm(IshtarForm): reverse_lazy('autocomplete-container'), associated_model=Container, new=True), validators=[valid_id(Container)]) + reference_container = forms.BooleanField( + label=_(u"Change the reference container"), required=False, + widget=widgets.CheckboxInput, initial=True, + help_text=_(u"If unchecked the current container will be changed") + ) create_treatment = forms.BooleanField( label=_(u"Create a treatment"), required=False, widget=widgets.CheckboxInput @@ -386,10 +396,13 @@ class QAFindTreatmentForm(IshtarForm): t.treatment_types.add(packaging) t.save(items=items) return + container_attr = 'container' + if self.cleaned_data.get('reference_container', False): + container_attr = 'container_ref' for find in items: - if find.container == container: + if getattr(find, container_attr) == container: continue - find.container = container + setattr(find, container_attr, container) find.save() diff --git a/archaeological_finds/migrations/0044_auto_20181201_1854.py b/archaeological_finds/migrations/0044_auto_20181201_1854.py new file mode 100644 index 000000000..e11a9db81 --- /dev/null +++ b/archaeological_finds/migrations/0044_auto_20181201_1854.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.10 on 2018-12-01 18:54 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('archaeological_warehouse', '0025_auto_20181112_1842'), + ('archaeological_finds', '0043_auto_20181130_1310'), + ] + + operations = [ + migrations.AlterModelOptions( + name='findbasket', + options={'permissions': (('view_find', 'Can view all Finds'), ('view_own_find', 'Can view own Find')), 'verbose_name': 'Basket'}, + ), + migrations.AddField( + model_name='find', + name='container_ref', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='finds_ref', to='archaeological_warehouse.Container', verbose_name='Reference container'), + ), + migrations.AddField( + model_name='historicalfind', + name='container_ref', + field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='archaeological_warehouse.Container'), + ), + migrations.AlterField( + model_name='treatmentfile', + name='associated_basket', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='treatment_files', to='archaeological_finds.FindBasket'), + ), + ] diff --git a/archaeological_finds/migrations/0045_migrate_current_container_to_ref_container.py b/archaeological_finds/migrations/0045_migrate_current_container_to_ref_container.py new file mode 100644 index 000000000..7639f95b7 --- /dev/null +++ b/archaeological_finds/migrations/0045_migrate_current_container_to_ref_container.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.10 on 2018-12-01 19:17 +from __future__ import unicode_literals + +from django.db import migrations + + +def migrate_containers(apps, schema): + Find = apps.get_model('archaeological_finds', 'find') + for f in Find.objects.filter(container__isnull=False).all(): + f.skip_history_when_saving = True + f.container_ref = f.container + f.save() + TreatmentType = apps.get_model('archaeological_finds', 'TreatmentType') + TreatmentType.objects.get_or_create( + txt_idx="loan", + defaults={ + "label": u"Prêt", + "virtual": False, + "comment": u"Un prêt est un changement temporaire de contenant " + u"pour du mobilier."} + ) + TreatmentType.objects.get_or_create( + txt_idx="loan-return", + defaults={ + "label": u"Retour de prêt", + "virtual": False, + "comment": u"Retour de mobilier dans son contenant de référence."} + ) + + +class Migration(migrations.Migration): + + dependencies = [ + ('archaeological_finds', '0044_auto_20181201_1854'), + ] + + operations = [ + migrations.RunPython(migrate_containers) + ] diff --git a/archaeological_finds/models_finds.py b/archaeological_finds/models_finds.py index b8f8d61a7..5dc8abc98 100644 --- a/archaeological_finds/models_finds.py +++ b/archaeological_finds/models_finds.py @@ -673,7 +673,8 @@ class Find(BulkUpdatedItem, ValueGetter, BaseHistorizedItem, OwnPerms, 'base_finds__context_record__label', 'material_types__label', 'object_types__label', 'datings__period__label', - 'container__cached_label', ] + 'container__cached_label', + 'container_ref__cached_label'] if settings.COUNTRY == 'fr': TABLE_COLS.insert( 3, 'base_finds__context_record__operation__code_patriarche') @@ -683,7 +684,7 @@ class Find(BulkUpdatedItem, ValueGetter, BaseHistorizedItem, OwnPerms, 'previous_id', 'label', 'material_types__label', 'datings__period__label', 'find_number', 'object_types__label', 'container__cached_label', - 'container__cached_location', + 'container_ref__cached_label', 'description', 'base_finds__context_record__town__name', 'base_finds__context_record__parcel', ] @@ -701,7 +702,7 @@ class Find(BulkUpdatedItem, ValueGetter, BaseHistorizedItem, OwnPerms, 'base_finds__context_record__archaeological_site__name': IshtarSiteProfile.get_default_site_label, 'base_finds__context_record__parcel': _(u"Parcel"), - 'base_finds__batch':_(u"Batch"), + 'base_finds__batch': _(u"Batch"), 'base_finds__comment': _(u"Base find - Comment"), 'base_finds__description': _(u"Base find - Description"), 'base_finds__topographic_localisation': _(u"Base find - " @@ -711,7 +712,8 @@ class Find(BulkUpdatedItem, ValueGetter, BaseHistorizedItem, OwnPerms, u"Base find - Discovery date (exact or TPQ)"), 'base_finds__discovery_date_taq': _( u"Base find - Discovery date (TAQ)"), - 'container__cached_label': _(u"Container"), + 'container__cached_label': _(u"Current container"), + 'container_ref__cached_label': _(u"Reference container"), 'datings__period__label': _(u"Periods"), 'material_types__label': _(u"Material types"), 'object_types__label': _(u"Object types"), @@ -894,20 +896,36 @@ class Find(BulkUpdatedItem, ValueGetter, BaseHistorizedItem, OwnPerms, pgettext_lazy("key for text search", u"has-image"), 'documents__image__isnull', ), - 'container__location': ( + 'container_ref__location': ( pgettext_lazy("key for text search", u"location"), + 'container_ref__location__name__iexact', + ), + 'container_ref__responsible': ( + pgettext_lazy("key for text search", u"warehouse"), + 'container_ref__responsible__name__iexact', + ), + 'container_ref__index': ( + pgettext_lazy("key for text search", u"container-index"), + 'container_ref__index', + ), + 'container_ref__reference': ( + pgettext_lazy("key for text search", u"container-ref"), + 'container_ref__reference__iexact', + ), + 'container__location': ( + pgettext_lazy("key for text search", u"current-location"), 'container__location__name__iexact', ), 'container__responsible': ( - pgettext_lazy("key for text search", u"warehouse"), + pgettext_lazy("key for text search", u"current-warehouse"), 'container__responsible__name__iexact', ), 'container__index': ( - pgettext_lazy("key for text search", u"container-index"), + pgettext_lazy("key for text search", u"current-container-index"), 'container__index', ), 'container__reference': ( - pgettext_lazy("key for text search", u"container-ref"), + pgettext_lazy("key for text search", u"current-container-ref"), 'container__reference__iexact', ), 'basket': ( @@ -1034,6 +1052,11 @@ class Find(BulkUpdatedItem, ValueGetter, BaseHistorizedItem, OwnPerms, "archaeological_warehouse.Container", verbose_name=_(u"Container"), blank=True, null=True, related_name='finds', on_delete=models.SET_NULL) + container_ref = models.ForeignKey( + "archaeological_warehouse.Container", + verbose_name=_(u"Reference container"), + blank=True, null=True, + related_name='finds_ref', on_delete=models.SET_NULL) is_complete = models.NullBooleanField(_(u"Is complete?"), blank=True, null=True) object_types = models.ManyToManyField( @@ -1541,21 +1564,62 @@ class Find(BulkUpdatedItem, ValueGetter, BaseHistorizedItem, OwnPerms, with connection.cursor() as c: c.execute(sql, args) - def get_localisation(self, place): + def get_localisation(self, place, is_ref=False): """ Get localisation reference in the warehouse :param place: number of the localisation starting with 0 + :param is_ref: if true - reference container else current container :return: reference - empty string if not available """ - if not self.container: + if is_ref: + container = self.container_ref + else: + container = self.container + if not container: return "" - locas = self.container.get_localisations() + locas = container.get_localisations() if len(locas) < (place + 1): return "" return locas[place] @property + def reference_localisation_1(self): + return self.get_localisation(0, is_ref=True) + + @property + def reference_localisation_2(self): + return self.get_localisation(1, is_ref=True) + + @property + def reference_localisation_3(self): + return self.get_localisation(2, is_ref=True) + + @property + def reference_localisation_4(self): + return self.get_localisation(3, is_ref=True) + + @property + def reference_localisation_5(self): + return self.get_localisation(4, is_ref=True) + + @property + def reference_localisation_6(self): + return self.get_localisation(5, is_ref=True) + + @property + def reference_localisation_7(self): + return self.get_localisation(6, is_ref=True) + + @property + def reference_localisation_8(self): + return self.get_localisation(7, is_ref=True) + + @property + def reference_localisation_9(self): + return self.get_localisation(8, is_ref=True) + + @property def localisation_1(self): return self.get_localisation(0) @@ -1591,19 +1655,75 @@ class Find(BulkUpdatedItem, ValueGetter, BaseHistorizedItem, OwnPerms, def localisation_9(self): return self.get_localisation(8) - def set_localisation(self, place, context, value): - if not self.container: + def set_localisation(self, place, context, value, is_ref=False): + """ + Get localisation reference in the warehouse + + :param place: number of the localisation starting with 0 + :param context: context of the request - not used + :param value: localisation value + :param is_ref: if true - reference container else current container + :return: None + """ + if is_ref: + container = self.container_ref + else: + container = self.container + + if not container: if not value: return - raise ImporterError(_(u"No container have been set - the " - u"localisation cannot be set.")) + if is_ref: + raise ImporterError( + _(u"No reference container have been set - the " + u"localisation cannot be set.")) + else: + raise ImporterError( + _(u"No container have been set - the localisation cannot " + u"be set.")) - localisation = self.container.set_localisation(place, value) + localisation = container.set_localisation(place, value) if value and value != '-' and not localisation: raise ImporterError( unicode(_(u"The division number {} have not been set " u"for the warehouse {}.")).format( - place + 1, self.container.location)) + place + 1, container.location)) + + @post_importer_action + def set_reference_localisation_1(self, context, value): + return self.set_localisation(0, context, value, is_ref=True) + + @post_importer_action + def set_reference_localisation_2(self, context, value): + return self.set_localisation(1, context, value, is_ref=True) + + @post_importer_action + def set_reference_localisation_3(self, context, value): + return self.set_localisation(2, context, value, is_ref=True) + + @post_importer_action + def set_reference_localisation_4(self, context, value): + return self.set_localisation(3, context, value, is_ref=True) + + @post_importer_action + def set_reference_localisation_5(self, context, value): + return self.set_localisation(4, context, value, is_ref=True) + + @post_importer_action + def set_reference_localisation_6(self, context, value): + return self.set_localisation(5, context, value, is_ref=True) + + @post_importer_action + def set_reference_localisation_7(self, context, value): + return self.set_localisation(6, context, value, is_ref=True) + + @post_importer_action + def set_reference_localisation_8(self, context, value): + return self.set_localisation(7, context, value, is_ref=True) + + @post_importer_action + def set_reference_localisation_9(self, context, value): + return self.set_localisation(8, context, value, is_ref=True) @post_importer_action def set_localisation_1(self, context, value): @@ -1682,6 +1802,9 @@ class Find(BulkUpdatedItem, ValueGetter, BaseHistorizedItem, OwnPerms, super(Find, self).save(*args, **kwargs) self.skip_history_when_saving = True + if self.container_ref and not self.container: + self.container = self.container_ref + updated = self.update_external_id(save=False) if updated: self._cached_label_checked = False diff --git a/archaeological_finds/models_treatments.py b/archaeological_finds/models_treatments.py index cc0d17dd0..1083b479b 100644 --- a/archaeological_finds/models_treatments.py +++ b/archaeological_finds/models_treatments.py @@ -458,16 +458,46 @@ class Treatment(DashboardFormItem, ValueGetter, BaseHistorizedItem, basket.items.remove(item) basket.items.add(new) - # manage containers - if not self.container: - return if create_new_find: q = Find.objects.filter(upstream_treatment=self) else: q = Find.objects.filter(treatments=self) + + # manage loan return + for tp in treatment_types: + if tp.txt_idx == 'loan-return': + for find in q.all(): + if find.container_ref: + find.container = find.container_ref + if find.pk in updated: + # don't record twice history + find.skip_history_when_saving = True + find.save() + + # manage containers + if not self.container: + return + + container_attr = None + for tp in treatment_types: + if tp.txt_idx == 'loan': + if container_attr: + # non consistent treatment + return + container_attr = 'container' + if tp.txt_idx == 'packaging': + if container_attr: + # non consistent treatment + return + container_attr = 'container_ref' + + if not container_attr: + # non consistent treatment + return + for find in q.all(): - if find.container != self.container: - find.container = self.container + if getattr(find, container_attr) != self.container: + setattr(find, container_attr, self.container) if find.pk in updated: # don't record twice history find.skip_history_when_saving = True diff --git a/archaeological_finds/templates/ishtar/forms/qa_find_treatment.html b/archaeological_finds/templates/ishtar/forms/qa_find_treatment.html index ef3906735..f20f0cb65 100644 --- a/archaeological_finds/templates/ishtar/forms/qa_find_treatment.html +++ b/archaeological_finds/templates/ishtar/forms/qa_find_treatment.html @@ -27,20 +27,26 @@ </div> <div class="form-row"> + {{ form.reference_container }} <label for="{{form.reference_container.auto_id}}"> + {% trans "Change the reference container" %} + </label> + </div> + + <div class="form-row"> {{ form.create_treatment }} <label for="{{form.create_treatment.auto_id}}"> {% trans "Associate a treatment" %} </label> </div> <div id="new-treatment"> - {% for field in form %} - {% if field.name != 'container' and field.name != 'create_treatment' %} - {% if forloop.counter0|divisibleby:2 %} + {% with force_large_col=True %}{% for field in form %} + {% if field.name != 'reference_container' and field.name != 'container' and field.name != 'create_treatment' %} + {% if forloop.counter|divisibleby:2 %} <div class="form-row">{% endif %} {% include "blocks/bs_field_snippet.html" %} {% if not forloop.counter0|divisibleby:2 %} </div>{% endif %} {% endif %} - {% endfor %} + {% endfor %}{% endwith %} </div> {% endblock %} diff --git a/archaeological_finds/templates/ishtar/sheet_find.html b/archaeological_finds/templates/ishtar/sheet_find.html index 4d6fe568c..1aa5220bf 100644 --- a/archaeological_finds/templates/ishtar/sheet_find.html +++ b/archaeological_finds/templates/ishtar/sheet_find.html @@ -39,7 +39,7 @@ </a> </li> {% endif %} - {% if item.container or item.upstream_treatment or item.downstream_treatment or item.treatments.count %} + {% if item.container or item.container_ref or item.upstream_treatment or item.downstream_treatment or item.treatments.count %} <li class="nav-item"> <a class="nav-link" id="{{window_id}}-warehouse-tab" data-toggle="tab" href="#{{window_id}}-warehouse" role="tab" @@ -228,8 +228,18 @@ </div> <div class="tab-pane fade" id="{{window_id}}-warehouse" role="tabpanel" aria-labelledby="{{window_id}}-warehouse-tab"> - {% if item.container %} - <h3>{% trans "Warehouse"%}</h3> + {% if item.container_ref %} + <h3>{% trans "Warehouse - reference container"%}</h3> + <div class='row'> + {% field_flex_detail "Container" item.container_ref %} + {% field_flex "Container ID" item.container_ref.cached_location %} + {% field_flex_detail "Responsible warehouse" item.container_ref.responsible %} + {% field_flex_detail "Location (warehouse)" item.container_ref.location %} + {% field_flex "Precise localisation" item.container_ref.cached_division %} + </div> + {% endif %} + {% if item.container and item.container_ref.pk != item.container.pk %} + <h3>{% trans "Warehouse - current container"%}</h3> <div class='row'> {% field_flex_detail "Container" item.container %} {% field_flex "Container ID" item.container.cached_location %} diff --git a/drassm_app b/drassm_app -Subproject afd2824264664d7bfecaa3702f495b0fe176fa4 +Subproject db4ff79c01fb71527bac6c00fd1d6831bb90d27 diff --git a/ishtar_common/templates/blocks/bs_field_snippet.html b/ishtar_common/templates/blocks/bs_field_snippet.html index 830dd4cfa..f46b15209 100644 --- a/ishtar_common/templates/blocks/bs_field_snippet.html +++ b/ishtar_common/templates/blocks/bs_field_snippet.html @@ -1,5 +1,5 @@ {% load i18n %} - <div class="form-group {% if field.field.widget.attrs.cols %}col-lg-12{% else %}col-lg-6{% endif %}{% if field.errors %} is-invalid{% endif %}{% if field.field.required %} required{% endif %}" + <div class="form-group {% if field.field.widget.attrs.cols or force_large_col %}col-lg-12{% else %}col-lg-6{% endif %}{% if field.errors %} is-invalid{% endif %}{% if field.field.required %} required{% endif %}" data-alt-name="{{field.field.alt_name}}"> {% if field.label %}{{ field.label_tag }}{% endif %} {% if show_field_number and field.field.order_number %}<span class="badge badge-pill badge-success field-tip"> |