mirror of
https://github.com/nophead/NopSCADlib.git
synced 2025-08-05 06:57:27 +02:00
bom.py now generates bom.csv to allow costed BOMs to be made using a spreadsheet.
This commit is contained in:
@@ -277,6 +277,15 @@ The top level assembly instructions and assembly contents could also be differen
|
|||||||
If the top level module is just a shell wrapper that simply includes one other assembly, with no additional parts, then it is removed from the build instructions and
|
If the top level module is just a shell wrapper that simply includes one other assembly, with no additional parts, then it is removed from the build instructions and
|
||||||
the assembly it calls becomes the top level. This allows a different project description for each target but only one set of top level instructions without repeating them.
|
the assembly it calls becomes the top level. This allows a different project description for each target but only one set of top level instructions without repeating them.
|
||||||
|
|
||||||
|
### Costed BOMs
|
||||||
|
|
||||||
|
A costed bill of materials can be made by opening the generated file `bom/bom.csv` in a spreadsheet program using a single quote as the string delimiter and comma as the field separator.
|
||||||
|
That gets a list of part descriptions and quantities to which prices can be added to get the total cost and perhaps a URL of where to buy each part.
|
||||||
|
|
||||||
|
If a Python file called `parts.py` is found then `bom.py` will attempt to call functions for each part to get a price and URL.
|
||||||
|
Any functions not found are printed, so you can see the format expected.
|
||||||
|
The function are passed the quantity to allow them to calculate volume discounts, etc.
|
||||||
|
|
||||||
### Other libraries
|
### Other libraries
|
||||||
|
|
||||||
The build scripts need to be able to locate the source files where the modules to generate the STL files and assemblies reside. They will search all the scad files
|
The build scripts need to be able to locate the source files where the modules to generate the STL files and assemblies reside. They will search all the scad files
|
||||||
|
16
examples/MainsBreakOutBox/bom/bom.csv
Normal file
16
examples/MainsBreakOutBox/bom/bom.csv
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
'Ferrule for 1.5mm^2 wire - not shown', 3
|
||||||
|
'Wire blue 30/0.25mm strands, length 150mm - not shown', 2
|
||||||
|
'Wire brown 30/0.25mm strands, length 150mm - not shown', 2
|
||||||
|
'Wire green & yellow 30/0.25mm strands, length 150mm - not shown', 2
|
||||||
|
'IEC inlet for ATX', 1
|
||||||
|
'Heatfit insert M3', 2
|
||||||
|
'4mm shielded jack socket blue', 2
|
||||||
|
'4mm shielded jack socket brown', 1
|
||||||
|
'4mm shielded jack socket green', 2
|
||||||
|
'Mains socket 13A', 1
|
||||||
|
'Nut M3 x 2.4mm nyloc', 6
|
||||||
|
'Screw M3 cs cap x 12mm', 2
|
||||||
|
'Screw M3 cs cap x 20mm', 2
|
||||||
|
'Screw M3 dome x 10mm', 4
|
||||||
|
'Heatshrink sleeving ID 3.2mm x 15mm - not shown', 8
|
||||||
|
'Washer M3 x 7mm x 0.5mm', 10
|
|
@@ -31,6 +31,12 @@ from set_config import *
|
|||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
try:
|
||||||
|
import parts
|
||||||
|
got_parts_py = True
|
||||||
|
except:
|
||||||
|
got_parts_py = False
|
||||||
|
|
||||||
def find_scad_file(mname):
|
def find_scad_file(mname):
|
||||||
for filename in os.listdir(source_dir):
|
for filename in os.listdir(source_dir):
|
||||||
if filename[-5:] == ".scad":
|
if filename[-5:] == ".scad":
|
||||||
@@ -129,6 +135,33 @@ class BOM:
|
|||||||
return ass
|
return ass
|
||||||
return ass.replace("assembly", "assemblies")
|
return ass.replace("assembly", "assemblies")
|
||||||
|
|
||||||
|
def print_CSV(self, file = None):
|
||||||
|
i = 0
|
||||||
|
for part in sorted(self.vitamins):
|
||||||
|
i += 1
|
||||||
|
if ': ' in part:
|
||||||
|
part_no, description = part.split(': ')
|
||||||
|
else:
|
||||||
|
part_no, description = "", part
|
||||||
|
qty = self.vitamins[part].count
|
||||||
|
if got_parts_py:
|
||||||
|
match = re.match(r'^.*\((.*?)[,\)].*$', part_no)
|
||||||
|
if match and not match.group(1).startswith('"'):
|
||||||
|
part_no = part_no.replace('(' + match.group(1), '_' + match.group(1) + '(').replace('(, ', '(')
|
||||||
|
func = 'parts.' + part_no.replace('(', '(%d, ' % qty).replace(', )', ')')
|
||||||
|
func = func.replace('true', 'True').replace('false', 'False').replace('undef', 'None')
|
||||||
|
try:
|
||||||
|
price, url = eval(func)
|
||||||
|
print("'%s',%3d,%.2f,'=B%d*C%d',%s" % (description, qty, price, i, i, url), file=file)
|
||||||
|
except:
|
||||||
|
if part_no:
|
||||||
|
print("%s not found in parts.py" % func)
|
||||||
|
print("'%s',%3d" % (description, qty), file=file)
|
||||||
|
else:
|
||||||
|
print("'%s',%3d" % (description, qty), file=file)
|
||||||
|
if got_parts_py:
|
||||||
|
print(",'=SUM(B1:B%d)',,'=SUM(D1:D%d)'" %(i, i), file=file)
|
||||||
|
|
||||||
def print_bom(self, breakdown, file = None):
|
def print_bom(self, breakdown, file = None):
|
||||||
if self.vitamins:
|
if self.vitamins:
|
||||||
print("Vitamins:", file=file)
|
print("Vitamins:", file=file)
|
||||||
@@ -265,6 +298,8 @@ def boms(target = None):
|
|||||||
|
|
||||||
main.print_bom(True, open(bom_dir + "/bom.txt","wt"))
|
main.print_bom(True, open(bom_dir + "/bom.txt","wt"))
|
||||||
|
|
||||||
|
main.print_CSV(open(bom_dir + "/bom.csv","wt"))
|
||||||
|
|
||||||
for ass in main.assemblies:
|
for ass in main.assemblies:
|
||||||
with open(bom_dir + "/" + ass + ".txt", "wt") as f:
|
with open(bom_dir + "/" + ass + ".txt", "wt") as f:
|
||||||
bom = main.assemblies[ass]
|
bom = main.assemblies[ass]
|
||||||
|
Reference in New Issue
Block a user