Subversion Repositories SmartDukaan

Rev

Rev 36962 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 36962 Rev 37060
Line 125... Line 125...
125
				* Math.sin(dLng / 2) * Math.sin(dLng / 2);
125
				* Math.sin(dLng / 2) * Math.sin(dLng / 2);
126
		double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
126
		double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
127
		return R * c;
127
		return R * c;
128
	}
128
	}
129
 
129
 
-
 
130
	// Reorder a day's bulk-upload stops in place into a greedy nearest-neighbor
-
 
131
	// route from the given start point (the beat's start location). Codes with no
-
 
132
	// known coordinates (offices, or partners missing lat/lng) are parked at the
-
 
133
	// end preserving their relative order rather than dropped. Sequence is taken
-
 
134
	// from the resulting list position by the caller.
-
 
135
	private static void sortPartnersByNearestNeighbor(List<BulkPartner> partners, Double startLat, Double startLng, Map<String, double[]> codeToLatLng) {
-
 
136
		if (partners == null || partners.size() < 2) return;
-
 
137
 
-
 
138
		List<BulkPartner> routable = new ArrayList<>();
-
 
139
		List<BulkPartner> parked = new ArrayList<>();
-
 
140
		for (BulkPartner p : partners) {
-
 
141
			if (codeToLatLng.containsKey(p.code)) routable.add(p);
-
 
142
			else parked.add(p);
-
 
143
		}
-
 
144
 
-
 
145
		List<BulkPartner> ordered = new ArrayList<>();
-
 
146
		double curLat, curLng;
-
 
147
		if (startLat != null && startLng != null) {
-
 
148
			curLat = startLat;
-
 
149
			curLng = startLng;
-
 
150
		} else if (!routable.isEmpty()) {
-
 
151
			BulkPartner first = routable.remove(0);
-
 
152
			ordered.add(first);
-
 
153
			double[] c = codeToLatLng.get(first.code);
-
 
154
			curLat = c[0];
-
 
155
			curLng = c[1];
-
 
156
		} else {
-
 
157
			return;
-
 
158
		}
-
 
159
 
-
 
160
		while (!routable.isEmpty()) {
-
 
161
			int bestIdx = 0;
-
 
162
			double bestD = Double.MAX_VALUE;
-
 
163
			for (int i = 0; i < routable.size(); i++) {
-
 
164
				double[] c = codeToLatLng.get(routable.get(i).code);
-
 
165
				double d = haversineKm(curLat, curLng, c[0], c[1]);
-
 
166
				if (d < bestD) {
-
 
167
					bestD = d;
-
 
168
					bestIdx = i;
-
 
169
				}
-
 
170
			}
-
 
171
			BulkPartner next = routable.remove(bestIdx);
-
 
172
			ordered.add(next);
-
 
173
			double[] c = codeToLatLng.get(next.code);
-
 
174
			curLat = c[0];
-
 
175
			curLng = c[1];
-
 
176
		}
-
 
177
 
-
 
178
		partners.clear();
-
 
179
		partners.addAll(ordered);
-
 
180
		partners.addAll(parked);
-
 
181
	}
-
 
182
 
130
    // Mirrors the JS recalcDay() formula. Used by schedule/repeat endpoints
183
    // Mirrors the JS recalcDay() formula. Used by schedule/repeat endpoints
131
    // which create fresh BeatSchedule rows — they need to fill totals from the
184
    // which create fresh BeatSchedule rows — they need to fill totals from the
132
    // existing beat_route table, not from anything the client posted.
185
    // existing beat_route table, not from anything the client posted.
133
    // Returns {totalKm, totalMins}.
186
    // Returns {totalKm, totalMins}.
134
    private double[] computeDayTotals(int beatId, int dayNumber, String endAction) {
187
    private double[] computeDayTotals(int beatId, int dayNumber, String endAction) {
Line 2231... Line 2284...
2231
		workbook.close();
2284
		workbook.close();
2232
 
2285
 
2233
		// Partner-code lookup (legacy).
2286
		// Partner-code lookup (legacy).
2234
		List<FofoStore> allStores = fofoStoreRepository.selectAll();
2287
		List<FofoStore> allStores = fofoStoreRepository.selectAll();
2235
		Map<String, Integer> codeToId = new HashMap<>();
2288
		Map<String, Integer> codeToId = new HashMap<>();
-
 
2289
		Map<String, double[]> codeToLatLng = new HashMap<>();
-
 
2290
		for (FofoStore store : allStores) {
2236
		for (FofoStore store : allStores) codeToId.put(store.getCode(), store.getId());
2291
			codeToId.put(store.getCode(), store.getId());
-
 
2292
			Double la = parseDoubleOrNull(store.getLatitude());
-
 
2293
			Double lo = parseDoubleOrNull(store.getLongitude());
-
 
2294
			if (la != null && lo != null) codeToLatLng.put(store.getCode(), new double[]{la, lo});
-
 
2295
		}
2237
 
2296
 
2238
		// Office-code lookup — office stops share the same `partner_code` column in the bulk
2297
		// Office-code lookup — office stops share the same `partner_code` column in the bulk
2239
		// sheet; resolution dispatches by which catalogue the code belongs to. A code present
2298
		// sheet; resolution dispatches by which catalogue the code belongs to. A code present
2240
		// in BOTH catalogues is treated as an error so the planner fixes the collision.
2299
		// in BOTH catalogues is treated as an error so the planner fixes the collision.
2241
		Map<String, Integer> officeCodeToId = new HashMap<>();
2300
		Map<String, Integer> officeCodeToId = new HashMap<>();
Line 2402... Line 2461...
2402
				schedule.setEndAction(dayIdx == sortedDays.size() - 1 ? "HOME" : "DAYBREAK");
2461
				schedule.setEndAction(dayIdx == sortedDays.size() - 1 ? "HOME" : "DAYBREAK");
2403
				schedule.setCreatedTimestamp(LocalDateTime.now());
2462
				schedule.setCreatedTimestamp(LocalDateTime.now());
2404
				beatScheduleRepository.persist(schedule);
2463
				beatScheduleRepository.persist(schedule);
2405
 
2464
 
2406
				List<BulkPartner> partners = g.dayToPartners.get(dayNumber);
2465
				List<BulkPartner> partners = g.dayToPartners.get(dayNumber);
2407
				partners.sort((a, b) -> {
2466
				// Auto-order the day into a nearest-neighbor route from the beat's
2408
					if (a.seq >= 0 && b.seq >= 0) return Integer.compare(a.seq, b.seq);
2467
				// start location, matching the interactive planner. The sheet's
-
 
2468
				// sequence_order column is intentionally ignored.
2409
					if (a.seq >= 0) return -1;
2469
				sortPartnersByNearestNeighbor(partners,
2410
					if (b.seq >= 0) return 1;
2470
						parseDoubleOrNull(beat.getStartLatitude()),
2411
					return Integer.compare(a.rowNum, b.rowNum);
2471
						parseDoubleOrNull(beat.getStartLongitude()),
2412
				});
2472
						codeToLatLng);
2413
 
2473
 
2414
				int autoSeq = 0;
2474
				int autoSeq = 0;
2415
				for (BulkPartner p : partners) {
2475
				for (BulkPartner p : partners) {
2416
					Integer partnerId = codeToId.get(p.code);
2476
					Integer partnerId = codeToId.get(p.code);
2417
					Integer officeId = officeCodeToId.get(p.code);
2477
					Integer officeId = officeCodeToId.get(p.code);
Line 2424... Line 2484...
2424
						route.setVisitType(com.spice.profitmandi.dao.enumuration.dtr.BeatVisitType.PARTNER);
2484
						route.setVisitType(com.spice.profitmandi.dao.enumuration.dtr.BeatVisitType.PARTNER);
2425
					} else {
2485
					} else {
2426
						route.setFofoId(officeId);
2486
						route.setFofoId(officeId);
2427
						route.setVisitType(com.spice.profitmandi.dao.enumuration.dtr.BeatVisitType.OFFICE);
2487
						route.setVisitType(com.spice.profitmandi.dao.enumuration.dtr.BeatVisitType.OFFICE);
2428
					}
2488
					}
-
 
2489
					// Sequence follows the nearest-neighbor ordering above, not the sheet.
2429
					route.setSequenceOrder(p.seq >= 0 ? p.seq : autoSeq);
2490
					route.setSequenceOrder(autoSeq);
2430
					route.setDayNumber(dayNumber);
2491
					route.setDayNumber(dayNumber);
2431
					route.setActive(true);
2492
					route.setActive(true);
2432
					beatRouteRepository.persist(route);
2493
					beatRouteRepository.persist(route);
2433
					autoSeq++;
2494
					autoSeq++;
2434
				}
2495
				}