• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

inventree / inventree-app / 12327582131

14 Dec 2024 06:22AM UTC coverage: 8.396% (+0.01%) from 8.386%
12327582131

Pull #578

github

web-flow
Merge 0781f4721 into 524c5469f
Pull Request #578: Refactor search widget

6 of 91 new or added lines in 4 files covered. (6.59%)

1 existing line in 1 file now uncovered.

727 of 8659 relevant lines covered (8.4%)

0.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

19.27
/lib/inventree/part.dart
1
import "dart:io";
2
import "dart:math";
3

4
import "package:flutter/material.dart";
5

6
import "package:inventree/api.dart";
7
import "package:inventree/helpers.dart";
8
import "package:inventree/l10.dart";
9

10
import "package:inventree/inventree/stock.dart";
11
import "package:inventree/inventree/company.dart";
12
import "package:inventree/inventree/model.dart";
13

14

15
/*
16
 * Class representing the PartCategory database model
17
 */
18
class InvenTreePartCategory extends InvenTreeModel {
19

20
  InvenTreePartCategory() : super();
2✔
21

22
  InvenTreePartCategory.fromJson(Map<String, dynamic> json) : super.fromJson(json);
2✔
23

24
  @override
1✔
25
  String get URL => "part/category/";
26

27
  static const String MODEL_TYPE = "partcategory";
28

29
  @override
×
30
  List<String> get rolesRequired => ["part_category"];
×
31

32
  @override
×
33
  Map<String, Map<String, dynamic>> formFields() {
34

35
    Map<String, Map<String, dynamic>> fields = {
×
36
      "name": {},
×
37
      "description": {},
×
38
      "parent": {},
×
39
      "structural": {},
×
40
    };
41

42
    return fields;
43
  }
44

45
  String get pathstring => getString("pathstring");
×
46
  
47
  String get parentPathString {
×
48

49
    List<String> psplit = pathstring.split("/");
×
50

51
    if (psplit.isNotEmpty) {
×
52
      psplit.removeLast();
×
53
    }
54

55
    String p = psplit.join("/");
×
56

57
    if (p.isEmpty) {
×
58
      p = L10().partCategoryTopLevel;
×
59
    }
60

61
    return p;
62
  }
63

64
  // Return the number of parts in this category
65
  // Note that the API changed from 'parts' to 'part_count' (v69)
66
  int get partcount => (jsondata["part_count"] ?? jsondata["parts"] ?? 0) as int;
×
67

68
  @override
1✔
69
  InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePartCategory.fromJson(json);
1✔
70
}
71

72

73
/*
74
 * Class representing the PartTestTemplate database model
75
 */
76
class InvenTreePartTestTemplate extends InvenTreeModel {
77

78
  InvenTreePartTestTemplate() : super();
×
79

80
  InvenTreePartTestTemplate.fromJson(Map<String, dynamic> json) : super.fromJson(json);
×
81

82
  @override
×
83
  String get URL => "part/test-template/";
84

85
  static const String MODEL_TYPE = "parttesttemplate";
86

87
  String get key => getString("key");
×
88

89
  String get testName => getString("test_name");
×
90

91
  bool get required => getBool("required");
×
92
  
93
  bool get requiresValue => getBool("requires_value");
×
94

95
  bool get requiresAttachment => getBool("requires_attachment");
×
96

97
  @override
×
98
  InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePartTestTemplate.fromJson(json);
×
99

100
  bool passFailStatus() {
×
101

102
    var result = latestResult();
×
103

104
    if (result == null) {
105
      return false;
106
    }
107

108
    return result.result;
×
109
  }
110

111
  // List of test results associated with this template
112
  List<InvenTreeStockItemTestResult> results = [];
113

114
  // Return the most recent test result recorded against this template
115
  InvenTreeStockItemTestResult? latestResult() {
×
116
    if (results.isEmpty) {
×
117
      return null;
118
    }
119

120
    return results.last;
×
121
  }
122

123
}
124

125
/*
126
 Class representing the PartParameter database model
127
 */
128
class InvenTreePartParameter extends InvenTreeModel {
129

130
  InvenTreePartParameter() : super();
×
131

132
  InvenTreePartParameter.fromJson(Map<String, dynamic> json) : super.fromJson(json);
×
133

134
  @override
×
135
  String get URL => "part/parameter/";
136

137
  @override
×
138
  List<String> get rolesRequired => ["part"];
×
139

140
  @override
×
141
  InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePartParameter.fromJson(json);
×
142

143
  @override
×
144
  Map<String, Map<String, dynamic>> formFields() {
145

146
    Map<String, Map<String, dynamic>> fields = {
×
147
      "header": {
×
148
        "type": "string",
149
        "read_only": true,
150
        "label": name,
×
151
        "help_text": description,
×
152
        "value": "",
153
      },
154
      "data": {
×
155
        "type": "string",
156
      }
157
    };
158

159
    return fields;
160
  }
161

162
  @override
×
163
  String get name => getString("name", subKey: "template_detail");
×
164

165
  @override
×
166
  String get description => getString("description", subKey: "template_detail");
×
167
  
168
  String get value => getString("data");
×
169
  
170
  String get valueString {
×
171
    String v = value;
×
172

173
    if (units.isNotEmpty) {
×
174
      v += " ";
×
175
      v += units;
×
176
    }
177

178
    return v;
179
  }
180

181
  bool get as_bool => value.toLowerCase() == "true";
×
182

183
  String get units => getString("units", subKey: "template_detail");
×
184
  
185
  bool get is_checkbox => getBool("checkbox", subKey: "template_detail", backup: false);
×
186
}
187

188
/*
189
 * Class representing the Part database model
190
 */
191
class InvenTreePart extends InvenTreeModel {
192

193
  InvenTreePart() : super();
4✔
194

195
  InvenTreePart.fromJson(Map<String, dynamic> json) : super.fromJson(json);
4✔
196

197
  @override
2✔
198
  String get URL => "part/";
199

200
  static const String MODEL_TYPE = "part";
201

202
  @override
×
203
  List<String> get rolesRequired => ["part"];
×
204

205
  @override
×
206
  Map<String, Map<String, dynamic>> formFields() {
207
    return {
×
208
      "name": {},
×
209
      "description": {},
×
210
      "IPN": {},
×
211
      "revision": {},
×
212
      "keywords": {},
×
213
      "link": {},
×
214

215
      "category": {},
×
216

217
      "default_location": {},
×
218

219
      "units": {},
×
220

221
      // Checkbox fields
222
      "active": {},
×
223
      "assembly": {},
×
224
      "component": {},
×
225
      "purchaseable": {},
×
226
      "salable": {},
×
227
      "trackable": {},
×
228
      "is_template": {},
×
229
      "virtual": {},
×
230
    };
231
  }
232

233
  @override
2✔
234
  Map<String, String> defaultFilters() {
235
    return {
2✔
236
      "category_detail": "true",
237
    };
238
  }
239

240
  // Cached list of stock items
241
  List<InvenTreeStockItem> stockItems = [];
242

243
  int get stockItemCount => stockItems.length;
×
244

245
  // Request stock items for this part
246
  Future<void> getStockItems(BuildContext context, {bool showDialog=false}) async {
×
247

248
    await InvenTreeStockItem().list(
×
249
      filters: {
×
250
        "part": "${pk}",
×
251
        "in_stock": "true",
252
      },
253
    ).then((var items) {
×
254
      stockItems.clear();
×
255

256
      for (var item in items) {
×
257
        if (item is InvenTreeStockItem) {
×
258
          stockItems.add(item);
×
259
        }
260
      }
261
    });
262
  }
263

264
  int get supplierCount => getInt("suppliers", backup: 0);
×
265
  
266
  // Request supplier parts for this part
267
  Future<List<InvenTreeSupplierPart>> getSupplierParts() async {
×
268
    List<InvenTreeSupplierPart> _supplierParts = [];
×
269

270
    final parts = await InvenTreeSupplierPart().list(
×
271
        filters: {
×
272
          "part": "${pk}",
×
273
        }
274
    );
275

276
    for (var result in parts) {
×
277
      if (result is InvenTreeSupplierPart) {
×
278
        _supplierParts.add(result);
×
279
      }
280
    }
281

282
    return _supplierParts;
283
  }
284

285
  // Cached list of test templates
286
  List<InvenTreePartTestTemplate> testingTemplates = [];
287

288
  int get testTemplateCount => testingTemplates.length;
×
289

290
  // Request test templates from the serve
291
  Future<void> getTestTemplates() async {
×
292

293
    InvenTreePartTestTemplate().list(
×
294
      filters: {
×
295
        "part": "${pk}",
×
296
      },
297
    ).then((var templates) {
×
298

299
      testingTemplates.clear();
×
300

301
      for (var t in templates) {
×
302
        if (t is InvenTreePartTestTemplate) {
×
303
          testingTemplates.add(t);
×
304
        }
305
      }
306
    });
307
  }
308

309
  int? get defaultLocation => jsondata["default_location"] as int?;
×
310

311
  double get onOrder => getDouble("ordering");
×
312

313
  String get onOrderString => simpleNumberString(onOrder);
×
314

315
  double get inStock {
1✔
316
    if (jsondata.containsKey("total_in_stock")) {
2✔
317
      return getDouble("total_in_stock");
1✔
318
    } else {
NEW
319
      return getDouble("in_stock");
×
320
    }
321
  }
322

323
  String get inStockString => simpleNumberString(inStock);
3✔
324

325
  // Get the 'available stock' for this Part
326
  double get unallocatedStock {
1✔
327

328
    double unallocated = 0;
329

330
    // Note that the 'available_stock' was not added until API v35
331
    if (jsondata.containsKey("unallocated_stock")) {
2✔
332
      unallocated = double.tryParse(jsondata["unallocated_stock"].toString()) ?? 0;
4✔
333
    } else {
NEW
334
      unallocated = inStock;
×
335
    }
336

337
    return max(0, unallocated);
1✔
338
  }
339

340
    String get unallocatedStockString => simpleNumberString(unallocatedStock);
3✔
341

342
    String stockString({bool includeUnits = true}) {
×
343
      String q = unallocatedStockString;
×
344

345
      if (unallocatedStock != inStock) {
×
346
        q += " / ${inStockString}";
×
347
      }
348

349
      if (includeUnits && units.isNotEmpty) {
×
350
        q += " ${units}";
×
351
      }
352

353
      return q;
354
    }
355

356
    String get units => getString("units");
×
357

358
    // Get the ID of the Part that this part is a variant of (or null)
359
    int? get variantOf => jsondata["variant_of"] as int?;
×
360

361
    // Get the number of units being build for this Part
362
    double get building => getDouble("building");
×
363

364
    // Get the number of BOMs this Part is used in (if it is a component)
365
    int get usedInCount => jsondata.containsKey("used_in") ? getInt("used_in", backup: 0) : 0;
×
366

367
    bool get isAssembly => getBool("assembly");
×
368

369
    bool get isComponent => getBool("component");
×
370

371
    bool get isPurchaseable => getBool("purchaseable");
×
372

373
    bool get isSalable => getBool("salable");
×
374

375
    bool get isActive => getBool("active");
×
376

377
    bool get isVirtual => getBool("virtual");
×
378

379
    bool get isTrackable => getBool("trackable");
×
380

381
    // Get the IPN (internal part number) for the Part instance
382
    String get IPN => getString("IPN");
×
383

384
    // Get the revision string for the Part instance
385
    String get revision => getString("revision");
×
386

387
    // Get the category ID for the Part instance (or "null" if does not exist)
388
    int get categoryId => getInt("category");
2✔
389

390
    // Get the category name for the Part instance
391
    String get categoryName {
1✔
392
      // Inavlid category ID
393
      if (categoryId <= 0) return "";
2✔
394

395
      if (!jsondata.containsKey("category_detail")) return "";
2✔
396

397
      return (jsondata["category_detail"]?["name"] ?? "") as String;
3✔
398
    }
399

400
    // Get the category description for the Part instance
401
    String get categoryDescription {
×
402
      // Invalid category ID
403
      if (categoryId <= 0) return "";
×
404

405
      if (!jsondata.containsKey("category_detail")) return "";
×
406

407
      return (jsondata["category_detail"]?["description"] ?? "") as String;
×
408
    }
409
    // Get the image URL for the Part instance
410
    String get _image  => getString("image");
2✔
411

412
    // Get the thumbnail URL for the Part instance
413
    String get _thumbnail => getString("thumbnail");
2✔
414

415
    // Return the fully-qualified name for the Part instance
416
    String get fullname {
1✔
417

418
      String fn = getString("full_name");
1✔
419

420
      if (fn.isNotEmpty) return fn;
1✔
421

422
      List<String> elements = [];
×
423

424
      if (IPN.isNotEmpty) elements.add(IPN);
×
425

426
      elements.add(name);
×
427

428
      if (revision.isNotEmpty) elements.add(revision);
×
429

430
      return elements.join(" | ");
×
431
    }
432

433
    // Return a path to the image for this Part
434
    String get image {
1✔
435
      // Use thumbnail as a backup
436
      String img = _image.isNotEmpty ? _image : _thumbnail;
3✔
437

438
      return img.isNotEmpty ? img : InvenTreeAPI.staticImage;
1✔
439
    }
440

441
    // Return a path to the thumbnail for this part
442
    String get thumbnail {
1✔
443
      // Use image as a backup
444
      String img = _thumbnail.isNotEmpty ? _thumbnail : _image;
3✔
445

446
      return img.isNotEmpty ? img : InvenTreeAPI.staticThumb;
1✔
447
    }
448

449
    Future<bool> uploadImage(File image) async {
×
450
      // Upload file against this part
451
      final APIResponse response = await InvenTreeAPI().uploadFile(
×
452
        url,
×
453
        image,
454
        method: "PATCH",
455
        name: "image",
456
      );
457

458
      return response.successful();
×
459
    }
460

461
    // Return the "starred" status of this part
462
    bool get starred => getBool("starred");
×
463

464
  @override
2✔
465
  InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePart.fromJson(json);
2✔
466
}
467

468
/*
469
 * Class representing an attachment file against a Part object
470
 */
471
class InvenTreePartAttachment extends InvenTreeAttachment {
472

473
  InvenTreePartAttachment() : super();
×
474

475
  InvenTreePartAttachment.fromJson(Map<String, dynamic> json) : super.fromJson(json);
×
476

477
  @override
×
478
  String get REFERENCE_FIELD => "part";
479

480
  @override
×
481
  String get REF_MODEL_TYPE => "part";
482

483
  @override
×
484
  String get URL => InvenTreeAPI().supportsModernAttachments ? "attachment/" : "part/attachment/";
×
485

486
  @override
×
487
  InvenTreeModel createFromJson(Map<String, dynamic> json) => InvenTreePartAttachment.fromJson(json);
×
488

489
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc