Subversion Repositories OpenARM Single-board Computer

Rev

Rev 330 | Rev 332 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
280 agaran 1
#!/usr/bin/perl -w
2
use strict;
293 agaran 3
# $Id: inventory.pl 331 2008-12-29 20:05:13Z agaran $
280 agaran 4
# Thu, 13 Nov 2008 21:06:23 +0100
5
# Maciej 'agaran' Pijanka <agaran@pld-linux.org>
6
# for OpenARM SBC Project
7
# license: gpl v3
8
 
293 agaran 9
 
280 agaran 10
use Getopt::Long qw//;
293 agaran 11
use File::Basename qw/basename/;
322 agaran 12
use IO::Dir;
13
use IO::File;
280 agaran 14
 
325 agaran 15
use lib File::Basename::dirname($0).'/lib';
326 agaran 16
use Text::Table;
325 agaran 17
 
18
 
313 agaran 19
my %Config;
280 agaran 20
 
313 agaran 21
# ==================================================
22
$Config{docdir} = '.';
23
$Config{bomdir} = '.';
24
$Config{verbose} = 1;
280 agaran 25
 
313 agaran 26
# 0 mean not show, -1 show all, positive value limits depth of shown
27
$Config{dbg_showdirs} = 0;
280 agaran 28
 
313 agaran 29
 
30
# modes
31
my $build_inventory = 0;
32
my $build_bom = 0;
33
my $show_conf = 0;
34
my $show_help = 0;
35
 
36
# ==================================================
37
 
38
my %Inv_By_PartNo;
39
my @Inv;
325 agaran 40
my %BomData;
313 agaran 41
 
293 agaran 42
sub err_printf($@) {
43
        my ($format, @args) = @_;
280 agaran 44
 
313 agaran 45
        printf STDERR "-E- ".$format."\n", @args;
46
        # exit? or fail-exit here
280 agaran 47
}
48
 
293 agaran 49
sub wrn_printf($@) {
50
        my ($format, @args) = @_;
328 agaran 51
        return if ($Config{verbose} <= 1) ;
293 agaran 52
        printf STDERR "-W- ".$format."\n", @args;
53
}
54
 
55
sub inf_printf($@) {
56
        my ($format, @args) = @_;
328 agaran 57
        return if ($Config{verbose} <= 2) ;
293 agaran 58
        printf STDERR "-I- ".$format."\n", @args;
59
}
60
 
313 agaran 61
sub Config_Show {
62
        printf "Config for %s\n----------------------------------------\n", basename($0);
63
        foreach my $name (sort keys %Config) {
64
                printf "%-20s %s\n", $name, $Config{$name};
65
        }
66
}
67
 
68
sub Help_Show {
69
        printf "Help for %s\n----------------------------------------\n", basename($0);
70
        unless (defined($_[1]) && length($_[1]) != 0) {
71
                print "Basic help\n\t--showrc|showconf shows current configuration\n".
72
                        "\t--docdir|-d <dir> tells script where information.txt files should be searched\n".
73
                        "\t--define <something>=<somethingelseornot> defines some configration value\n".
319 agaran 74
                        "\t--verbose|-v [level] sets verbosity level\nOrder of options DOES matter\n".
330 agaran 75
                        "\t--bomdir|-b <dir> tells script where boms should be searched\n".
331 agaran 76
                        "\t--outfile|-o <file> tells where script shall save output data\n".
330 agaran 77
                        "\t--force|-f forces script to save data even if file exist already\n";
319 agaran 78
 
79
                print "\t\e[0;31mThis Help Is created especially for \e[0;33mJelle\e[0;29m\n".
80
                        "\tThis script search all directories below specified ones to find interesting files\n";
313 agaran 81
                return;
82
        }
83
        if ($_[1] =~ /^foo$/) {
84
                print "Noo, there is no foo's here\n";
85
        } else {
86
                printf "Sorry, help for `%s' don't exist (eventually) yet\n", $_[1];
87
        }
88
        exit;
89
}
90
 
91
sub fix_dir ($) {
92
        my $dir = shift @_;
93
 
94
        $dir =~ s/\/$//;
95
 
96
        if (! -d $dir) {
97
                err_printf("Sorry `%s' is not valid directory, exiting", $dir);
98
        }
99
 
100
        return $dir;
101
}
102
 
103
sub shortdir ($) {
104
        my $path = shift @_;
105
 
106
        return substr($path, 2) if ($path =~ /^\.\//) ;
107
        return $path;
108
}
109
 
110
 
111
sub trim($) {
112
        my ($value) = @_;
113
 
114
        $value =~ s/^ +//;
115
        $value =~ s/ +$//;
116
        return $value;
117
}
118
 
119
sub etrim($) {
120
        my ($value) = @_;
121
 
122
        $value =~ s/^[  ]+//;
123
        $value =~ s/[   ]+$//;
124
        return $value;
125
}
126
 
330 agaran 127
sub strbreak($$) {
325 agaran 128
        my ($str,$lim) = @_;
330 agaran 129
        my @p = split / /,$str;
130
        $str = '';
131
        my $l = 0;
132
        while (@p) {
133
                my $e = shift @p;
134
                if ($l + length ($e) +1 > $lim) {
135
                        $str .= "\n".$e;
136
                        $l = length $e;
137
                } else {
138
                        $str .= " ".$e;
139
                        $l += length($e) + 1;
140
                }
141
        }
325 agaran 142
        return $str;
143
}
144
 
313 agaran 145
# this subroutine is used as callback function
146
# executed by file_lookup
280 agaran 147
sub parse_ifile($) {
293 agaran 148
        my ($filepath) = @_;
280 agaran 149
 
318 agaran 150
 
293 agaran 151
        open(IN, $filepath) or return 1;
280 agaran 152
 
293 agaran 153
        my %data;
280 agaran 154
 
293 agaran 155
        while (not eof IN) {
156
                my $line = <IN>;
280 agaran 157
 
293 agaran 158
                chomp $line;
280 agaran 159
 
293 agaran 160
                next if ($line =~ /^[   ]*$/);
161
                next if ($line =~ /^;/);
162
 
163
                last if ($line =~ /^--/);
164
 
165
                if ($line =~ /^([A-Za-z ]+):(.*)$/) {
313 agaran 166
                        my ($name,$value) = (lc etrim($1),etrim($2));
293 agaran 167
 
168
                        if ($name =~ /^price$/) {
169
                                $value =~ s/[^0-9\.\,]//g;
170
                                if ($value =~ s/^([0-9]+)[,.]([0-9]+)$/$1.$2/) {
171
                                        # printf STDERR "Price %.3f\n", $value;
172
                                        if (!defined($data{price})) {
173
                                                $data{price} = $value;
174
                                        } else {
313 agaran 175
                                                wrn_printf("Duplicated price field in file %s", shortdir($filepath));
293 agaran 176
                                        }
177
                                } else {
313 agaran 178
                                        err_printf("Bad price field in file %s", shortdir($filepath));
293 agaran 179
                                }
180
                        } elsif ($name =~ /^manufacturer$/i) {
181
                                # printf STDERR "Manufacturer %s\n", $value;
182
                                if (!defined($data{manufacturer})) {
183
                                        $data{manufacturer} = $value;
184
                                } else {
313 agaran 185
                                        wrn_printf("Duplicated manufacturer field in file %s", shortdir($filepath));
293 agaran 186
                                }
187
                        } elsif ($name =~ /^manufacturer part no$/ ) {
188
                                # printf STDERR "ManPartNo %s\n", $value;
189
                                if (!defined($data{manufact_partno})) {
313 agaran 190
                                        $data{manufact_partno} = trim($value);
293 agaran 191
                                } else {
192
                                        wrn_printf("Duplicated manufacturer part no field in file %s",
313 agaran 193
                                                shortdir($filepath));
293 agaran 194
                                }
195
                        } elsif ($name =~ /^description$/i) {
313 agaran 196
                                if (!defined($data{desc})) {
197
                                        $data{desc} = $value;
198
                                } else {
199
                                        wrn_printf("Duplicated description no field in file %s",
200
                                                shortdir($filepath));
201
                                }
293 agaran 202
                        } elsif ($name =~ /^datasheet$/i) {
203
                                $data{datasheet} = [] unless defined $data{datasheet};
204
                                push @{$data{datasheet}}, $value;
205
                                # printf STDERR "Datasheet %s\n", $value;
206
                        } elsif ($name =~ /^supplier$/i) {
207
                                # printf STDERR "Supplier %s\n", $value;
208
                                if (!defined($data{supplier})) {
209
                                        $data{supplier} = $value;
210
                                } else {
211
                                        wrn_printf("Duplicated supplier field in file %s",
313 agaran 212
                                                shortdir($filepath));
293 agaran 213
                                }
214
                        } elsif ($name =~ /^order code$/) {
215
                                # printf STDERR "Order Code %s\n", $value;
216
                                if (!defined($data{ordercode})) {
217
                                        $data{ordercode} = $value;
218
                                } else {
219
                                        wrn_printf("Duplicated order code field in file %s",
313 agaran 220
                                                shortdir($filepath));
293 agaran 221
                                }
222
                                #push @DATA, { $name => $value };
223
                        } elsif ($name =~ /^url .*$/) {
224
                                # printf STDERR "URL %s\n", $value;
225
                        } elsif ($name =~ /^catalog(ue|) page$/) {
226
                                # printf STDERR "Catalogue Page %s\n", $value;
227
                        } else {
228
                                err_printf("Unhandled field %s in file %s", $name,
313 agaran 229
                                        shortdir($filepath));
293 agaran 230
                        }
231
 
232
                } else {
328 agaran 233
                        wrn_printf("Unparseable line `%s', forgot ; to set it as comment in file %s", $line,
313 agaran 234
                                shortdir($filepath));
293 agaran 235
                }
236
        }
237
        close(IN);
238
 
239
        if (scalar keys %data == 0) {
240
                inf_printf("Skipping file %s because contain no data for me",
313 agaran 241
                        shortdir($filepath));
293 agaran 242
                return;
243
        }
244
 
245
        unless (defined ($data{price}) && $data{price} != 0) {
246
                wrn_printf("Missing Price in file %s",
313 agaran 247
                        shortdir($filepath));
293 agaran 248
        }
249
 
250
        unless (defined($data{manufact_partno})) {
251
                inf_printf("Missing Manufacturer Part No in file %s",
313 agaran 252
                        shortdir($filepath));
293 agaran 253
        }
254
 
255
        unless (defined($data{ordercode})) {
256
                wrn_printf("Missing Order Code in file %s",
313 agaran 257
                        shortdir($filepath));
293 agaran 258
        }
259
 
313 agaran 260
 
293 agaran 261
        use Data::Dumper qw/Dumper/;
313 agaran 262
 
263
        my $id = scalar @Inv;
264
 
265
        $Inv[$id] = {};
266
 
267
        $Inv[$id]{Datasheet} = delete $data{'datasheet'} if defined $data{'datasheet'};
268
        $Inv[$id]{Manufacturer} = delete $data{'manufacturer'} if defined $data{'manufacturer'};
269
        $Inv[$id]{Description} = delete $data{'desc'} if defined $data{'desc'};
270
        $Inv[$id]{Price} = delete $data{'price'} if defined $data{'price'};
271
        $Inv[$id]{Ordercode} = delete $data{'ordercode'} if defined $data{'ordercode'};
272
        $Inv[$id]{Manufacturer_Partno} = delete $data{'manufact_partno'} if defined $data{'manufact_partno'};
273
        $Inv[$id]{Supplier} = delete $data{'supplier'} if defined $data{'supplier'};
274
 
275
        unless (defined ($Inv_By_PartNo{$Inv[$id]{Manufacturer_Partno}})) {
276
                $Inv_By_PartNo{$Inv[$id]{Manufacturer_Partno}} = $id;
277
        } else {
278
                wrn_printf("PartNumber %s happened more than once, using first occurence (id:%d)",
279
                        $Inv[$id]{Manufacturer_Partno}, $id);
280
        }
325 agaran 281
 
313 agaran 282
        #inf_printf("Part %s defined in file %s", $Inv[$id]{Manufacturer_Partno}, shortdir($filepath));
293 agaran 283
 
313 agaran 284
        wrn_printf("Unhandled data from parsing: %s", Dumper(\%data)) if (scalar keys %data > 0);
280 agaran 285
}
286
 
287
 
313 agaran 288
sub parse_bom ($) {
289
        my ($filepath) = @_;
280 agaran 290
 
313 agaran 291
        open(IN, $filepath) or return 1;
292
 
293
        #wrn_printf("GotARg: %s", shortdir($filepath));
294
 
295
        my @Fields;
296
 
297
        my %data;
298
        my $v = '';
299
        while (not eof IN) {
300
                my $line = <IN>;
301
 
302
                chomp $line;
303
 
304
                if ($line =~ /^\.START$/) {
305
                        $v = 'boms';
306
                        next;
307
                }
308
 
309
                if ($line =~ /^\.END$/) {
310
                        $v = '';
311
                        next;
312
                }
313
 
314
                if ($v eq 'boms') {
315
                        @Fields = split(/\t/, substr($line,2));
316
                        $v = 'bom';
317
                        # some funny way to generate field-map
318
                        # that if someone reorder bom file columns we are still on place
319
                        next;
320
                }
321
 
322
                if ($v eq 'bom') {
323
                        my ($refdes, $device, $value, $footprint, $quantity) = split (/\t/, $line);
324
 
325
                        $device = trim($device);
326
 
327
#                       wrn_printf("Device: %s (value: %s) at refdes %s, %d pcs", $device, $value, $refdes, $quantity);
328
 
329
                        if (!defined $Inv_By_PartNo{$device}) {
330
                                wrn_printf("Device %s not found in inventory in file %s", $device, shortdir($filepath));
331
                                next;
332
                        }
333
                        my $id = $Inv_By_PartNo{$device};
334
                        next if ($Inv[$id]{Manufacturer} =~ /none/i); # skip parts whose manufacturer is none
335
                        #inf_printf("Found in Inventory at %d %s", $id, $Inv[$id]{Manufacturer_Partno});
336
                        push @{$data{$id}{RefDes}}, $refdes;
330 agaran 337
                        if (!defined $data{$id}{Footprint}) {
338
                                $data{$id}{Footprint} = $footprint;
339
                        } else {
340
                                if ($data{$id}{Footprint} ne $footprint) {
341
                                        err_printf("Different footprints for same device in within single bom file, script ".
342
                                                "cannot work around this, will use first one, but expect that output file ".
343
                                                "might have errors.");
344
                                }
345
                        }
313 agaran 346
                }
347
        }
348
        close(IN);
349
 
350
        if (scalar keys %data == 0) {
351
                inf_printf("Skipping file %s because contain no data for me",
352
                        shortdir($filepath));
353
                return;
354
        }
355
 
356
        foreach my $id (keys %data) {
357
                my %tmp;
358
                map { $tmp{$_} = 1 } @{$data{$id}{RefDes}};
359
                @{$data{$id}{RefDes}} = keys %tmp;
360
                my $cnt = scalar @{$data{$id}{RefDes}};
361
                if (!defined $Inv[$id]{Price}) {
362
                        wrn_printf("%s has no price, setting to 0.0", $Inv[$id]{Manufacturer_Partno});
363
                        $Inv[$id]{Price} = 0;
364
                }
318 agaran 365
                push @{$BomData{$id}{RefDes}}, @{$data{$id}{RefDes}};
330 agaran 366
                if (!defined $BomData{$id}{Footprint}) {
367
                        $BomData{$id}{Footprint} = $data{$id}{Footprint};
368
                } else {
369
                        if ($data{$id}{Footprint} ne $data{$id}{Footprint}) {
370
                                err_printf("Different footprints for same device between sheets, script cannot ".
371
                                        "work around this, will use first one, but expect that output file might ".
372
                                        "have errors.");
373
                        }
374
                }
375
                push @{$BomData{$id}{Files}}, substr(basename($filepath),0,length(basename($filepath))-4);
318 agaran 376
 
313 agaran 377
#               printf "%-20s %.4f %s\n", $Inv[$id]{Manufacturer_Partno}, $cnt, $icost, join (', ', @{$data{$id}{RefDes}});
378
#               %BomData{ById}{$id}{RefDes}
379
        }
380
}
381
 
382
sub file_lookup ($$$$) ;
383
sub file_lookup ($$$$) {
384
        my ($dir, $depth, $regexp, $callback) = @_;
385
 
386
        err_printf("Sorry, callback must be CODE ref") unless (ref $callback eq 'CODE');
387
 
293 agaran 388
        if ( -d $dir) {
322 agaran 389
                my $d = IO::Dir->new($dir);
390
                return 1 if (!defined $d);
391
 
392
 
393
                while (defined(my $e = $d->read)) {
293 agaran 394
                        my $fe = $dir .'/'. $e;
395
                        if ( -f $fe) {
313 agaran 396
                                if ($fe =~ $regexp) {
397
                                        &$callback($fe);
293 agaran 398
                                }
399
                        } elsif (-d $fe) { # now its dir...
400
                                if ($e eq '.svn') { # if entry name is equal to svn
401
                                        next; # go to next entry in foreach loop
402
                                }
403
                                next if ($e eq '.' or $e eq '..'); # skip to next if dir entry is . or ..
281 jelle 404
 
313 agaran 405
                                if ($Config{dbg_showdirs} == -1 or $Config{dbg_showdirs} > $depth) {
406
                                        printf STDERR "Entering directory %s\n", shortdir($fe);
293 agaran 407
                                }
313 agaran 408
                                return 1 if (file_lookup($fe, $depth+1, $regexp, $callback) == 1);
293 agaran 409
                        } else {
313 agaran 410
                                # symlink or other mysterius beast
293 agaran 411
                        }
412
                }
413
        }
313 agaran 414
        return 0;
280 agaran 415
}
416
 
293 agaran 417
Getopt::Long::Configure("bundling");
280 agaran 418
 
319 agaran 419
if (scalar @ARGV == 0) {
420
        Help_Show();
421
        exit;
422
}
423
 
313 agaran 424
my $result = Getopt::Long::GetOptions (
425
        "showrc|showconf" => sub { $show_conf = 1 },
426
        "docdir|d=s" => sub { $Config{docdir} = $_[1]; },
427
        # not sure if bomdir or SCH dir
428
        "bomdir|b=s" => sub { $Config{$_[0]} = $_[1]; },  # $_[0] contain basename of option, so in few cases could be (ab)used
429
        "define|D=s" => sub { my ($p,$q) = split(/=/,$_[1],2); $Config{$p} = $q; },
430
        "verbose|v:+" => sub { $Config{$_[0]} = ($_[1]>1?0:$Config{$_[0]}) + $_[1]; },
330 agaran 431
        "help|h|?:s" => sub { $show_help = 1; },
432
        "outfile|o=s" => sub { $Config{$_[0]} = $_[1]; },
433
        "force|f" => sub { $Config{$_[0]} = 1 },
313 agaran 434
 
435
        # options 
293 agaran 436
);
437
if (!$result) {
438
        printf "Usage: %s [-d directory] [-v]\n",basename($0);
439
        exit;
440
}
313 agaran 441
 
442
# ==================================================
443
# processing of options/config values, checking ranges etc
444
# 
445
 
446
$Config{docdir} = fix_dir ($Config{docdir});
447
$Config{bomdir} = fix_dir ($Config{bomdir});
448
 
449
if ( $show_help == 1) {
450
        Help_Show();
451
        exit;
293 agaran 452
}
453
 
313 agaran 454
if ( $show_conf == 1) {
455
        Config_Show();
456
        exit;
457
}
293 agaran 458
 
313 agaran 459
# make Inventory
328 agaran 460
printf STDERR "Indexing information.txt (under %s)\n",shortdir($Config{docdir});
313 agaran 461
file_lookup($Config{docdir}, 0, qr/\/information.txt$/, \&parse_ifile);
328 agaran 462
printf STDERR "\tFinished, %d entries loaded\n", scalar(@Inv)+1;
313 agaran 463
 
464
# process BOM files
328 agaran 465
printf STDERR "Loading bom data from %s\n", shortdir($Config{bomdir});
313 agaran 466
file_lookup($Config{bomdir}, 0, qr/\.bom$/, \&parse_bom);
328 agaran 467
printf STDERR "\tLoaded, now processing\n";
313 agaran 468
 
330 agaran 469
my ($bn,$pn) = (1,1);
318 agaran 470
my $cost = 0.0;
471
 
330 agaran 472
my $out;
473
if (!defined $Config{outfile}) {
474
        $Config{outfile} = './output.txt';
475
        wrn_printf("Output file not specified, saving out in ".$Config{outfile});
476
}
318 agaran 477
 
330 agaran 478
if ( -e $Config{outfile}) {
479
        unless (defined $Config{force} && $Config{force} == 1) {
480
                inf_printf("Unlinking output.txt before owrewriting");
481
                unlink($Config{outfile});
482
        } else {
483
                wrn_printf("Output file already exist, add --force if i shall overwrite it");
484
                exit;
485
        }
486
}
318 agaran 487
 
330 agaran 488
$out = new IO::File $Config{outfile}, 'w';
318 agaran 489
 
325 agaran 490
my $bomtable = Text::Table->new(
327 agaran 491
        { title => '| ', is_sep => 1 },
329 jelle 492
        { title => 'id', align => 'right', align_title => 'left' },
327 agaran 493
        { title => ' | ', is_sep => 1 },
329 jelle 494
        { title => 'description', align => 'left', align_title => 'left' },
327 agaran 495
        { title => ' | ', is_sep => 1 },
329 jelle 496
        { title => 'manufacturer partid', align => 'left', align_title => 'left' },
327 agaran 497
        { title => ' | ', is_sep => 1 },
329 jelle 498
        { title => 'manufacturer', align => 'left', align_title => 'left' },
327 agaran 499
        { title => ' | ', is_sep => 1 },
329 jelle 500
        { title => 'order code', align => 'left', align_title => 'left' },
327 agaran 501
        { title => ' | ', is_sep => 1 },
329 jelle 502
        { title => 'quantity', align => 'right', align_title => 'left' },
327 agaran 503
        { title => ' | ', is_sep => 1 },
329 jelle 504
        { title => "price", align => 'right', align_title => 'left' },
327 agaran 505
        { title => ' | ', is_sep => 1 },
329 jelle 506
        { title => "cost", align => 'right', align_title => 'left' },
327 agaran 507
        { title => ' |', is_sep => 1 },
325 agaran 508
);
330 agaran 509
 
510
my $parttable = Text::Table->new(
511
        { title => '| ', is_sep => 1 },
512
        { title => 'id', align => 'right', align_title => 'center' },
513
        { title => ' | ', is_sep => 1 },
514
        { title => 'description', align => 'left', align_title => 'center' },
515
        { title => ' | ', is_sep => 1 },
516
        { title => 'manufacturer partno', align => 'left', align_title => 'center' },
517
        { title => ' | ', is_sep => 1 },
518
        { title => 'footprint', align => 'left', align_title => 'center' },
519
        { title => ' | ', is_sep => 1 },
520
        { title => 'refdes', align => 'left', align_title => 'center' },
521
        { title => ' | ', is_sep => 1 },
522
        { title => 'sheet', align => 'left', align_title => 'center' },
523
        { title => ' |', is_sep => 1 },
524
);
325 agaran 525
 
526
foreach my $id ( sort {my $p = $Inv[$a]{Manufacturer} cmp $Inv[$b]{Manufacturer}; if ($p == 0) {
527
#               return $Inv[$a]{Manufacturer_Partno} cmp $Inv[$b]{Manufacturer_Partno}}; return $p; }  keys %BomData) {
528
                return $Inv[$a]{Description} cmp $Inv[$b]{Description}}; return $p; }  keys %BomData) {
318 agaran 529
        my %tmp;
530
        map { $tmp{$_} = 1 } @{$BomData{$id}{RefDes}};
531
        @{$BomData{$id}{RefDes}} = keys %tmp;
325 agaran 532
        my $quant = scalar @{$BomData{$id}{RefDes}};
318 agaran 533
        if (!defined $Inv[$id]{Price}) {
534
                wrn_printf("%s has no price, setting to 0.0", $Inv[$id]{Manufacturer_Partno});
535
                $Inv[$id]{Price} = 0;
536
        }
325 agaran 537
        my $icost = $quant * $Inv[$id]{Price};
318 agaran 538
 
330 agaran 539
#       printf PARTMAP "%-35s|%s\n", shortstring($Inv[$id]{Description},35), join (', ', sort @{$BomData{$id}{RefDes}});
318 agaran 540
        $cost += $icost;
541
#       %BomData{ById}{$id}{RefDes}
327 agaran 542
#       $bomtable->add(' '.$n.' ', $Inv[$id]{Description}.' ', $Inv[$id]{Manufacturer_Partno}.' ', $Inv[$id]{Manufacturer}.' ',
543
#               $Inv[$id]{Ordercode}.' ', ' '.$quant.' ', sprintf("%.3f ",$Inv[$id]{Price}), sprintf("%.3f ",$icost));
330 agaran 544
        $bomtable->add($bn++, $Inv[$id]{Description}, $Inv[$id]{Manufacturer_Partno}, $Inv[$id]{Manufacturer},
327 agaran 545
                $Inv[$id]{Ordercode},$quant, sprintf("%.3f",$Inv[$id]{Price}), sprintf("%.3f",$icost));
330 agaran 546
 
547
        $parttable->add($pn++, $Inv[$id]{Description}, $Inv[$id]{Manufacturer_Partno}, $BomData{$id}{Footprint},
548
                strbreak(join (', ', sort @{$BomData{$id}{RefDes}}),43), join("\n",@{$BomData{$id}{Files}}));
318 agaran 549
}
550
 
325 agaran 551
printf $out "file generated at %s\n\n", scalar localtime(time());
552
 
553
print $out $bomtable->rule('-','+');
554
print $out $bomtable->title();
555
print $out $bomtable->rule('-','+');
556
print $out $bomtable->body();
557
print $out $bomtable->rule('-','+');
558
 
330 agaran 559
printf $out "\nTotal cost: %.3f\n\n\n", $cost;
325 agaran 560
 
330 agaran 561
print $out $parttable->rule('-','+');
562
print $out $parttable->title();
563
print $out $parttable->rule('-','+');
564
print $out $parttable->body();
565
print $out $parttable->rule('-','+');
325 agaran 566
 
330 agaran 567
 
568
#close PARTMAP;
569
 
570
printf STDERR "\tFinished, output saved in %s\n", $Config{outfile};