it_manager/locations: редактор шаблона этикеток, раздельная печать QR/EAN и карточка поставщика.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-07 16:40:48 +03:00
parent 82ad90c661
commit d54f6fbe42
18 changed files with 1192 additions and 66 deletions
+6 -2
View File
@@ -118,7 +118,7 @@ class PortalVendorForm(forms.ModelForm):
fields = (
'name', 'location_region',
'street', 'house_number', 'building', 'apartment',
'comments', 'is_active',
'location', 'comments', 'is_active',
)
widgets = {
'name': forms.TextInput(attrs={
@@ -130,6 +130,10 @@ class PortalVendorForm(forms.ModelForm):
'house_number': forms.TextInput(attrs={'class': 'input'}),
'building': forms.TextInput(attrs={'class': 'input'}),
'apartment': forms.TextInput(attrs={'class': 'input'}),
'location': forms.TextInput(attrs={
'class': 'input',
'placeholder': '46.4825,30.7233',
}),
'comments': forms.Textarea(attrs={'class': 'input', 'rows': 2}),
'is_active': forms.CheckboxInput(),
}
@@ -138,7 +142,7 @@ class PortalVendorForm(forms.ModelForm):
super().__init__(*args, **kwargs)
self.fields['name'].required = True
self.fields['location_region'].widget.attrs['class'] = 'input'
for optional in ('street', 'house_number', 'building', 'apartment', 'comments'):
for optional in ('street', 'house_number', 'building', 'apartment', 'comments', 'location'):
self.fields[optional].required = False
def clean_name(self):
+31
View File
@@ -802,3 +802,34 @@ class DislocationVendorsTabTests(TestCase):
self.vendor.refresh_from_db()
self.assertEqual(self.vendor.name, 'ТестСервис Обновлён')
self.assertEqual(self.vendor.comments, 'ремонт техники')
def test_vendor_detail_card_and_map(self):
from django.test import Client
from core.test_utils import login_2fa
client = Client()
login_2fa(client, self.viewer)
r = client.get(f'/dislocation/vendors/{self.vendor.pk}/', HTTP_HX_REQUEST='true')
self.assertEqual(r.status_code, 200)
self.assertContains(r, 'ТестСервис')
self.assertContains(r, 'Координаты не указаны')
login_2fa(client, self.it_user)
r2 = client.post(f'/dislocation/vendors/{self.vendor.pk}/update/', {
'name': 'ТестСервис',
'location_region': str(self.region.pk),
'location': '46.482500,30.723300',
'is_active': 'on',
'from_card': '1',
}, HTTP_HX_REQUEST='true')
self.assertEqual(r2.status_code, 200)
self.vendor.refresh_from_db()
# Дефолт поля location — как раз Одесса; has_coordinates False для дефолта.
# Ставим недефолтные координаты.
self.vendor.location = '46.500000,30.750000'
self.vendor.save(update_fields=['location', 'address'])
r3 = client.get(f'/dislocation/vendors/{self.vendor.pk}/', HTTP_HX_REQUEST='true')
self.assertEqual(r3.status_code, 200)
self.assertContains(r3, 'vendor-map')
self.assertContains(r3, '46')
self.assertContains(r3, '30,75000')
+42 -1
View File
@@ -165,6 +165,34 @@ def pharmacy_update_view(request, pk):
return render_page(request, 'pharmacy-detail.html', context)
def _vendor_detail_context(request, vendor, *, form=None, edit_mode=False,
form_message=None, form_error=None):
can_edit = user_can_manage_vendors(request.user)
if edit_mode and can_edit and form is None:
form = PortalVendorForm(instance=vendor)
return {
'vendor': vendor,
'can_edit_vendor': can_edit,
'vendor_edit_mode': bool(edit_mode and can_edit),
'vendor_edit_form': form if (edit_mode and can_edit) else None,
'vendor_form_message': form_message,
'vendor_form_error': form_error,
}
@login_required
def vendor_detail_view(request, pk):
vendor = get_object_or_404(
Location.objects.select_related('location_region'),
pk=pk, location_type='vendor',
)
edit_mode = request.GET.get('edit') == '1'
if edit_mode and not user_can_manage_vendors(request.user):
return HttpResponseForbidden()
context = _vendor_detail_context(request, vendor, edit_mode=edit_mode)
return render_page(request, 'vendor-detail.html', context)
@login_required
@require_POST
def vendor_create_view(request):
@@ -188,9 +216,22 @@ def vendor_update_view(request, pk):
vendor = get_object_or_404(Location, pk=pk, location_type='vendor')
form = PortalVendorForm(request.POST, instance=vendor)
# Если пришли с карточки — остаёмся на ней
from_card = request.POST.get('from_card') == '1'
if form.is_valid():
form.save()
vendor = form.save()
if from_card:
context = _vendor_detail_context(
request, vendor, form_message='Изменения сохранены.',
)
return render_page(request, 'vendor-detail.html', context)
return _dislocation_vendors_response(request, message='Изменения сохранены.')
if from_card:
context = _vendor_detail_context(
request, vendor, form=form, edit_mode=True,
form_error=_vendor_form_errors(form),
)
return render_page(request, 'vendor-detail.html', context)
return _dislocation_vendors_response(
request, error=_vendor_form_errors(form), form=form,
)