Tonnaro
Tonnaro ზიდავს იმას, რასაც ჩვეულებრივი გადამზიდავები არ იღებენ: ამწეებს, დაბალპლატფორმიან ტრალებს, ბეტონის ტუმბოებს, არაგაბარიტულ ტვირთს. პლატფორმა თავიდან ბოლომდე ჩვენ ავაგეთ — Django/DRF ბექენდი კონფიგურირებადი ფასწარმოქმნის ძრავით და მასზე დაშენებული სამი React ფრონტენდი: საჯარო მარკეტინგული საიტი, ტელეფონის ფორმატის შეკვეთის აპლიკაცია მომხმარებლებისთვის და ოპერაციების კონსოლი დისპეტჩერებისთვის. ყველაფერი — ქართულად, ინგლისურად და რუსულად.
წელი: 2026 · კატეგორია: ლოჯისტიკა
ჩვენი როლი
Full-stack. დავაპროექტეთ მონაცემთა მოდელი, ფასწარმოქმნის ძრავა და დანიშვნის ლოგიკა, ხოლო მთელი ფრონტენდი ჩვენ ავაგეთ.
პრობლემა
საქართველოში სპეციალიზებული ტრანსპორტის შეკვეთა სატელეფონო ზარებით ხდებოდა. მომხმარებელი, რომელსაც გზაზე ექსკავატორი დარჩა ან მესამე სართულზე ბეტონის ჩასხმა სჭირდება, ოპერატორებს ერთიმეორის მიყოლებით ურეკავდა, სამუშაოს რამდენჯერმე აღწერდა და ფასის ნაცვლად ვარაუდებს იღებდა — რადგან რეალური ღირებულება დამოკიდებულია მანქანის კლასზე, მანძილზე, ტვირთზე და, ქვეყანაში, რომელიც უმეტესად მთაა, იმაზე, რამდენი სიმაღლის აღმართი უნდა დაძლიოს სატვირთომ.
ოპერატორის მხარესაც უკეთ არაფერი იყო. რომელი ამწეა ხუთშაბათს თავისუფალი, რომელ მძღოლს აქვს მისი მართვის უფლება და ხომ არ არის ეს სამუშაო უკვე სხვისთვის დაპირებული — ეს ყველაფერი დისპეტჩერის თავში ინახებოდა. არაფერი იწერებოდა, სანამ რამე უკვე არ აირეოდა.
მიდგომა
პლატფორმა ერთ იდეაზეა აგებული: ფასის შეთავაზება გამოთვლილი უნდა იყოს და არა დამახსოვრებული.
შეკვეთა სამუშაოს აფიქსირებს — სერვისი, ტვირთი, აყვანისა და დანიშნულების წერტილები რუკაზე — ბექენდი კი მას მძიმე სატვირთო მანქანის მარშრუტად აგებს, ამ მარშრუტიდან სიმაღლის პროფილს კითხულობს და ფასწარმოქმნის ძრავაში ატარებს, რომელსაც ადმინი აკონფიგურირებს და რომელიც კოდში ჩაწერილი არ გვაქვს. დისპეტჩერი ხედავს შემოთავაზებულ ფასს, იმ მანქანების მოკლე სიას, რომელთა მძღოლებსაც მართლაც აქვთ შესაბამისი მოწმობა, და უკვე დაჯავშნილ ყველაფერთან კონფლიქტების შემოწმების შედეგს.
შემდეგ ეს მდგომარეობების მანქანაა, რომლის შუაშიც შეთავაზება დგას: ადმინი ფასს ადებს და აგზავნის, მომხმარებელი თანხმდება და მხოლოდ ამის შემდეგ ხდება რესურსის დაკავება. ყოველი ნაბიჯი ფიქსირდება, ამიტომ კითხვას „ვინ შეცვალა ეს და როდის“ პასუხი აქვს.
მთავარი მომენტები
- ფასწარმოქმნა კონფიგურირებადი ძრავაა და არა ტარიფების ცხრილი: ზონების წესები განიხილება იმ თანმიმდევრობით, რომლითაც ადმინმა ისინი გადაათრია, და გამარჯვებული ზონა ტრანსპორტის ტიპი × ზონა ცხრილიდან ტარიფის მთელ სტრიქონს ირჩევს. ჯამს ქმნის წონის კომპონენტი (ფასი კილოგრამზე), მანძილის მუხლზე სიმაღლის მამრავლი, დღეების მამრავლი და სართულის ფიქსირებული დანამატი; კომპანიის საკომისიო და დღგ ამ ჯამიდან გამოიყოფა და მას თავზე არ ემატება. თითოეული მათგანი რედაქტირებადი ცხრილია, გვერდით — რეალურ დროში მომუშავე ფასის კალკულატორით.
- მარშრუტები სატვირთოს გათვალისწინებით იგება. ყოველი შეკვეთის ღირებულება HGV მარშრუტზე, სიმაღლის რეალური პროფილით ითვლება, რადგან 40-ტონიანი ტვირთი, რომელიც მტკვრის ხეობიდან 2,000 მ-ს ადის, იმდენი არ ჯდება, რამდენიც იგივე მანძილი ვაკეზე.
- სამუშაოს თავისუფალი ტექსტით აღწერისას სისტემა სწორ სერვისს სთავაზობს — „მანქანა გზატკეცილზე გამიფუჭდა“ ევაკუატორზე მიუთითებს — საკვანძო სიტყვების ძრავით, რომელსაც ადმინი თითოეული სერვისისთვის ცალკე არედაქტირებს.
- მანქანისა და მძღოლის დანიშვნა მოწმდება და ნდობაზე არ არის აგებული: მძღოლის მართვის მოწმობის კატეგორიები მანქანას უნდა ფარავდეს, ხოლო ერთმანეთს გადაფარული ჯავშნები უარყოფილია და ერთი და იგივე რესურსი ორჯერ არ იყიდება.
- მთელი პროდუქტი სამენოვანია თვით კონტენტის მოდელამდე — ყოველი სერვისი, კატეგორია და ლენდინგის სექცია ინახავს {en, ka, ru}-ს, ამიტომ ადმინი სამივეს ერთ ფორმაში არედაქტირებს.
- საჯარო საიტს ადმინი არედაქტირებს: ჰირო-ბლოკი, სერვისები, სტატისტიკა, SEO ტეგები და მთელი აპლიკაციის აქცენტის ფერი მონაცემებია და არა დეპლოი.
- შეკვეთებს საკუთარი აუდიტის ჟურნალი აქვს — სტატუსის ყოველი ცვლილება და ადმინის ყოველი რედაქტირება ველების დონეზე ფიქსირდება და ორივე მხარეს უჩანს.
არქიტექტურა
- მომხმარებლის აპლიკაცია: ტელეფონის ფორმის React-ნაკადი — და არა რესპონსიული დესკტოპ-ფორმა. მრავალნაბიჯიანი ვიზარდი, რომელიც თავისუფალი ტექსტის აღწერიდან სერვისს სთავაზობს, საკუთარ ველებს თითოეული სერვისისთვის ცვლის, აყვანისა და დანიშნულების წერტილებს რუკის ნიშნულებად იღებს მრავალგაჩერებიანი მარშრუტის მხარდაჭერით, და შეკვეთის გაგზავნამდე აჩვენებს მარშრუტს, აღმართს და სავარაუდო ფასს.
- ოპერაციების კონსოლი: დისპეტჩერის სამუშაო სივრცე: შენახული ხედების ჩანართები რეალურ დროში განახლებადი რაოდენობებით, პოლინგზე დაფუძნებული შეტყობინებების ზარი, რომელიც მხოლოდ მეორე მხარის ქმედებებზე რეკავს, შეკვეთაზე დანიშვნა შესაბამისობისა და კონფლიქტების შემოწმებით, და სერვერზე გენერირებული CSV ექსპორტი, რომელიც ფორმულით დაწყებულ უჯრებს ესკეიპირებს, ნაცვლად იმისა, რომ ელცხრილს ინექცია მიაწოდოს.
- ფასწარმოქმნის ძრავა: ზონები, ტარიფები ტრანსპორტის ტიპის მიხედვით, წონისა და მანძილის კომპონენტები, სიმაღლის დიაპაზონები, აღჭურვილობის დამატებები, კომპანიის საკომისიო და დღგ — თითოეული რედაქტირებადი ცხრილია. ერთი და იგივე ძრავა ემსახურება მომხმარებლის რეალურ დროში გამოთვლილ სავარაუდო ფასს, ადმინის ფასის კალკულატორს და ხელახალ გამოთვლას, რომელიც მარშრუტის დადგენისას ეშვება — ამიტომ ორი ეკრანი ერთ რიცხვზე ვერასოდეს იდავებს.
- მარშრუტიზაცია: სერვერული OpenRouteService პროქსი driving-hgv მიმართულებებისთვის სიმაღლითა და დახრილობით: გასაღები სერვერზე ემატება და პასუხები ქეშირდება, ამიტომ API გასაღები ბრაუზერამდე არასოდეს აღწევს, ხელახალი რენდერი კი ზედმეტ მოთხოვნად არ ჯდება. ფრონტენდი ხაზავს დახრილობის მიხედვით გაფერადებულ პოლიხაზს და მის ქვეშ — გაგლუვებულ სიმაღლის გრაფიკს.
- კონტენტი და იდენტობა: ლენდინგი, საიტის პარამეტრები, წესები და პირობები და SEO ოთხი სინგლტონია მრავალენოვანი JSON ველებით. ფერთა თემა, საიტის სახელი, favicon და საკონტაქტო ინფორმაცია ფრონტენდს ჩატვირთვისას მიეწოდება, ამიტომ პროდუქტის ვიზუალის შეცვლა ადმინის ქმედებაა.
- ბექენდი: თერთმეტი Django აპლიკაცია, 22 მოდელი, ყველგან DRF generics ცალსახა per-view უფლებებით, JWT refresh-ტოკენის როტაციითა და შავი სიით, UUID-ით გადარქმეული ატვირთვები სიგნალებზე დაფუძნებული გასუფთავებით და მოთხოვნების სიხშირის ლიმიტები ყველა მგრძნობიარე ენდფოინთზე.
ციფრებში
API მარშრუტები: 128 · Django მოდელები: 22 · ფრონტენდის კოდის ხაზები: 33k · ენები: 3
შედეგები
- ფასის დადგენა სატელეფონო ზარიდან გამოთვლილ რიცხვზე გადავიდა, რომელსაც მომხმარებელი გადაწყვეტილების მიღებამდე ხედავს.
- დისპეტჩერებმა ორმაგი დაჯავშნა შეწყვიტეს: კონფლიქტების შემოწმება გადაფარვას უარყოფს და არა მოგვიანებით ავლენს.
- სერვისის, ზონის ან ტარიფის დამატება ადმინის ქმედება გახდა და არა დეპლოი.
- ერთი პლატფორმა სამ აუდიტორიას ემსახურება — საჯარო ვიზიტორებს, მომხმარებლებსა და დისპეტჩერებს — სამ ენაზე, ერთი კონტენტის მოდელიდან.
რა გამოვიყენეთ
- ბექენდი: Django 4.2, Django REST Framework, SimpleJWT, PostgreSQL, Pillow
- ფრონტენდი: React 18.3, Create React App, Ant Design 5.15, React Router 6.28, axios, dayjs, DOMPurify, dnd-kit, react-quill-new
- რუკები, მარშრუტიზაცია და გრაფიკები: Leaflet 1.9, react-leaflet 4.2, OpenRouteService (driving-hgv, სიმაღლე + დახრილობა), Nominatim, OpenStreetMap რუკის ფილები, Recharts 2.12
- ინფრასტრუქტურა: Docker, nginx, DigitalOcean
დემო
რეალური ფრონტენდი, რომლის Django ბექენდიც ბრაუზერშივე მომუშავე სიმულირებული ვერსიით არის ჩანაცვლებული: საწყისი მონაცემები ბანდლშია ჩაშენებული და მეხსიერებიდან მიეწოდება. ყოველი ჩაწერა მუშაობს და გვერდის გადატვირთვამდე ინახება. ბანერიდან შედით როგორც მომხმარებელი ან როგორც დისპეტჩერი. ქსელიდან მხოლოდ რუკის ფილები იტვირთება; მარშრუტიზაცია, გეოკოდირება და ფასწარმოქმნა სრულად ჩანართშივე მუშაობს.