1 #+OPTIONS: H:3 num:nil toc:t \n:nil @:t ::t |:t ^:t -:t f:t *:t TeX:t LaTeX:t skip:nil d:(HIDE) tags:not-in-toc
2 #+STARTUP: align fold nodlcheck hidestars oddeven lognotestate
3 #+SEQ_TODO: TODO(t) INPROGRESS(i) WAITING(w@) | DONE(d) CANCELED(c@)
4 #+TAGS: Write(w) Update(u) Fix(f) Check(c)
5 #+TITLE: BOM : Bills-of-materials
6 #+AUTHOR: Christian Fortin
7 #+EMAIL: frozenlock AT gmail DOT com
9 #+CATEGORY: worg-tutorial
11 [[file:index.org][{Back to Worg's index}]]
16 This add-on collects components across the entire org buffer (even
17 in drawers), making it easy to retrieve and sort data. It uses the
18 column special name as a landmark. We will refer to them as
19 'Keywords'. The keywords are searched using a string-match function,
20 which gives the ability to have multiple column with the same
21 functionality, but also to use the column name as we would usually
22 with org-mode. For example, we can have 'tag' and 'tag2', both are
23 recognized by the BOM add-on and can be used in a spreadsheet-like
24 formula without any confusion. The keywords are also
25 case-insensitive. 'Component' and 'component' will work in the same
28 The BOM is usually used with a dynamic block. (You can use the
29 different functions in emacs-lisp code, but this is beyond the
30 purpose of this tutorial.) Here is the basic dynamic block:
35 And here is what we obtain at this point:
37 : | Section | Tag | Component | Quantity |
38 : |---------+-----+-----------+----------|
41 The table is empty, because we have to either:
42 1. Add keywords in a table;
43 2. Add a line-component.
48 This is the most important keyword and act as the trigger. For
49 this example, let's say we write down things we want to buy. In
50 this case, a new keyboard for our computer. This is how the
58 The '!' character is used in org table to specify column name, such
59 as our keyword, 'component'.
60 And here is what the bill-of-materials for this table is:
63 : | Section | Tag | Component | Quantity |
64 : |-----------+-----+-----------+----------|
65 : | Component | | Keyboard | 1 |
68 As you can see, the heading (Component) was automatically
69 used as the 'section', which doesn't require attention for
70 now. The quantity is, unsurprisingly, 1. There is nothing in the tag
71 column for now, so let's dismiss it by adding the parameter *:no-tag
73 This will results in the following:
74 : #+BEGIN: bom :no-tag t
75 : | Section | Component | Quantity |
76 : |-----------+-----------+----------|
77 : | Component | Keyboard | 1 |
80 Now suppose that our friend too wants a new keyboard.
82 : | | For | Material |
84 : |---+--------+-----------|
86 : | | Friend | Keyboard |
88 : #+BEGIN: bom :no-tag t
89 : | Section | Component | Quantity |
90 : |-----------+-----------+----------|
91 : | Component | Keyboard | 2 |
94 As expected, we get 2 keyboards.
98 The section is used to separate what would otherwise be an
99 identical component. Suppose we don't want our friend's wishes to be
100 in the same BOM as our, but still have them in the same table.
102 : | | For | Material |
103 : | ! | Section | Component |
104 : |---+---------+-----------|
105 : | | Me | Keyboard |
106 : | | Friend | Keyboard |
108 This will results in the following BOM:
110 : #+BEGIN: bom :no-tag t
111 : | Section | Component | Quantity |
112 : |---------+-----------+----------|
113 : | Friend | Keyboard | 1 |
114 : | Me | Keyboard | 1 |
117 Please note that when a component is given a section, it isn't
118 associated with the heading anymore. As an alternative, you can set
119 a ':SECTION:' property in the heading, which will be inherited by
120 all the components _without_ a specified section.
121 Section's priorities are as follow:
123 1. Given section with the 'section' keyword;
124 2. The SECTION property;
129 With this keyword, it is possible to specify a quantity for the
130 associated component. In our always improving scenario, we now want to
131 give a keyboard to another of our friend (as a gift). This is going to
132 be bought at the same time as our keyboard, so they belong together.
134 : | | For | Material | |
135 : | ! | Section | Component | Qty |
136 : |---+---------+-----------+-----|
137 : | | Me | Keyboard | 2 |
138 : | | Friend | Keyboard | 1 |
140 : #+BEGIN: bom :no-tag t
141 : | Section | Component | Quantity |
142 : |---------+-----------+----------|
143 : | Friend | Keyboard | 1 |
144 : | Me | Keyboard | 2 |
147 *Important*: If Qty keyword is present, then any empty field will
148 be considered as _zero_. This way, multiple column quantity are
151 : | | For | Material | Personal | Gift |
152 : | ! | Section | Component | Qty | Qty2 |
153 : |---+---------+-----------+----------+------|
154 : | | Me | Keyboard | 1 | 1 |
155 : | | Friend | Keyboard | 1 | |
157 : #+BEGIN: bom :no-tag t
158 : | Section | Component | Quantity |
159 : |---------+-----------+----------|
160 : | Friend | Keyboard | 1 |
161 : | Me | Keyboard | 2 |
166 When a BOM starts to get big, we often need a quick reminder of
167 why we need certain component. Another use is also to identify the
168 component. As the Qty keyword, multiple Tag columns can be associated
169 with a single component. Here we will simply use the tag as a reminder
170 of what we want to look for in the store.
172 : | | For | Material | Personal | Gift | Need |
173 : | ! | Section | Component | Qty | Qty2 | Tag |
174 : |---+---------+-----------+----------+------+--------------------|
175 : | | Me | Keyboard | 1 | 1 | Matching colors |
176 : | | Friend | Keyboard | 1 | | Dinosaurs pictures |
178 To show the tag column in the BOM, we simply remove the no-tag
181 : | Section | Tag | Component | Quantity |
182 : |---------+--------------------+-----------+----------|
183 : | Friend | Dinosaurs pictures | Keyboard | 1 |
184 : | Me | Matching colors | Keyboard | 2 |
188 If two Tag columns are present for a single Component column, the
189 tags will be associated with this component, separated by a comma.
191 * Renaming BOM columns
193 It is possible to rename the BOM columns with the following
199 - col-name-description
202 This is how our renamed BOM would look like:
204 : #+BEGIN: bom :col-name-section For :col-name-tag Need :col-name-component Stuff :col-name-quantity Qty
205 : | For | Need | Stuff | Qty |
206 : |--------+--------------------+----------+-----|
207 : | Friend | Dinosaurs pictures | Keyboard | 1 |
208 : | Me | Matching colors | Keyboard | 2 |
211 * Multiple component's column
213 There is two way to add components in a section. Either by adding
214 other rows with the same section's name, or by adding other
215 columns. Both have their uses and they should come to you quite
216 naturally. In our example, we want more stuff.
218 : | | For | Material | Personal | Gift | Need | Stuff | More stuff | Much more stuff | How many |
219 : | ! | Section | Component | Qty | Qty2 | Tag | Component | Component | Component | Qty |
220 : |---+---------+-----------+----------+------+--------------------+-----------+------------+-----------------+----------|
221 : | | Me | Keyboard | 1 | 1 | Matching colors | Mouse | Headset | USB flash drive | 23 |
222 : | | Friend | Keyboard | 1 | | Dinosaurs pictures | | | | |
223 : | | Friend | | | | | | | CDs | 50 |
224 : | | Friend | Mouse | 1 | | | | | | |
226 This is beginning to get interesting. Note that even if we can
227 name the additional columns 'Component2' or 'ComponentAAA',
228 there's no use to do it if no table-formula uses the column
231 * Precise section selection
232 Now suppose we want to get OUR to-buy list. Simply specify
233 the section's parameter *:section Me*:
235 : #+BEGIN: bom :section Me
236 : | Tag | Component | Quantity |
237 : |-----------------+-----------------+----------|
239 : | Matching colors | Keyboard | 2 |
241 : | | USB flash drive | 23 |
244 Wait, where's the section column? Well we don't need it anymore,
247 A '+' sign will specify we want more than a single section. *:section
248 Me+Friend* will select both section, and add the quantity and tags
251 : #+BEGIN: bom :section Me+Friend
252 : | Tag | Component | Quantity |
253 : |-------------------------------------+-----------------+----------|
256 : | Dinosaurs pictures, Matching colors | Keyboard | 3 |
258 : | | USB flash drive | 23 |
261 *Do not* put a whitespace between the section name and the '+' sign.
262 Speaking of whitespace, if you need one in a section name, simply
264 : #+BEGIN: bom :section "Section with whitespace"
266 We can also return every section that matches at least what we
267 provide. To activate this, use *:part-match t*. With this, if we
268 write "fr", the Friend section is returned. If we had another
269 section named "Frosting", than Friend and Frosting would have been
270 merged and we would have a total for both section.
272 : #+BEGIN: bom :section fr :part-match t
273 : | Tag | Component | Quantity |
274 : |--------------------+-----------+----------|
276 : | Dinosaurs pictures | Keyboard | 1 |
280 It is also possible to specify that we don't want any section
281 containing "fr". For this, use the parameter *:remove t*.
283 : #+BEGIN: bom :section fr :part-match t :remove t
284 : | Tag | Component | Quantity |
285 : |-----------------+-----------------+----------|
287 : | Matching colors | Keyboard | 2 |
289 : | | USB flash drive | 23 |
292 In this case, getting all sections not containing "fr" is the
293 equivalent of choosing the section "Me".
295 If you simply want the components from the current heading, use the
296 parameter *:local-only t*. This will return components with the
297 current heading as their section, which is the default of every
298 component if no section is provided. If a section has been provided to
299 a component (and is not exactly equal to the heading), the component
300 will not be returned.
302 Here, we don't have any component under this heading:
303 : #+BEGIN: bom :local-only t
304 : | Tag | Component | Quantity |
305 : |-----+-----------+----------|
309 This is all really interesting, but when we're in a shop, we want
310 to know how many of each item we have to buy, we need a *total*.
311 For this, simply add the *:total t* parameter. We will also remove
312 the tags once again by using *:no-tag t*.
314 : #+BEGIN: bom :total t :no-tag t
315 : | Component | Quantity |
316 : |-----------------+----------|
321 : | USB flash drive | 23 |
324 This is the equivalent of merging every sections together.
325 * Adding a component without a table
327 There is another option you might need. If you ever want to
328 add a component without a table, use the #+BOM commentary. As any
329 other org-mode commentary, this one won't appear when exported to
330 another document (pdf, html, docbook..). It will, however, enable
331 you to add a single component in the bill-of-materials. Here is an
333 : #+BOM: Keyboard :section Need :tag "Matching colors"
335 As with the table components, you can simply give a component name if
336 you desire. If no section is given, it will be inherited as an
337 ordinary component in a table: a section property or the current
341 There is two way to add details to a BOM. The first one is to setq
342 `org-bom-details' with a plist containing, depending on your
343 needs, :description, :datasheet-pdf and :price. You must, however, at
344 least have the component name in the :name property. Here is an
345 example on how to set this variable:
347 #+BEGIN_SRC emacs-lisp
348 (setq org-bom-details '((:name "Keyboard" :description
349 "Something" :price "40")
350 (:name "CDs" :description "Not
351 DVDs" :datasheet-pdf "CD.pdf")))
353 Please note that the price is a *string*.
355 The other method, valid for the current buffer only, is to give one
356 or more bom-details table. It is recognized when a table is named as
358 : #+TBLNAME: bom-details
360 Once again, the column names are used. Contrary to the normal BOM
361 keywords however, these are case-sensitive and must be written
362 exactly as their property name. For example, the column of the
363 property ':name' must be 'name'.
364 : #+TBLNAME: bom-details
365 : | ! | name | description | price |
366 : |---+----------+--------------+-------|
367 : | | Keyboard | Used to type | 40 |
370 Any bom-details table will temporarily overshadow the
371 `org-bom-details' variable, but won't erase or modify it. This means
372 you can safely use a bom-details table if you need to change some
373 local buffer description, while using `org-bom-details' in multiple
376 Look at the CDs description. When a field is empty, it is *not* used
377 and BOM falls back to the property in the `org-bom-details'
382 You can add a description column in a BOM by adding the
383 *:description t* parameter.
385 : #+BEGIN: bom :total t :no-tag t :description t
386 : | Component | Quantity | Description |
387 : |-----------------+----------+--------------|
388 : | CDs | 50 | Not DVDs |
389 : | Headset | 1 | N/A |
390 : | Keyboard | 3 | Used to type |
391 : | Mouse | 2 | N/A |
392 : | USB flash drive | 23 | N/A |
395 See how the CDs' description wasn't the empty field from the
400 You can add a price column in a BOM by adding the *:price t*
403 : #+BEGIN: bom :total t :no-tag t :description t :price t
404 : | Component | Quantity | Price | Description |
405 : |-----------------+----------+-------+--------------|
406 : | CDs | 50 | | Not DVDs |
407 : | Headset | 1 | | N/A |
408 : | Keyboard | 3 | 120 | Used to type |
409 : | Mouse | 2 | | N/A |
410 : | USB flash drive | 23 | | N/A |
411 : |-----------------+----------+-------+--------------|
412 : | TOTAL: | | 120 | |
413 : #+TBLFM: @>$3=vsum(@I..@>>)
415 The price is automatically multiplied by the quantity of each
416 component. In addition, a total row is added at the table's bottom
417 with a vertical sum formula.
421 This is a special property and must be used only if you intend to
422 export in a pdf document. See [[LaTeX mode and bom-datasheet]] for more details.
424 * List of BOM parameters
425 Here is a list of all the parameters usable in a BOM dynamic block,
426 as seen throughout this tutorial:
428 - no-tag :: Remove the tags column
429 - section :: Select this section (or more if there's a + sign)
430 - part-match :: Select every section with at least the string
431 provided for the section parameter
432 - remove :: Select every sections except the one(s) provided
433 - descripton :: Add the description column
434 - price :: Add the price column and a total row at the bottom of the
436 - col-name-*** :: Rename the associated column
437 * Advanced and elisp functions
439 Each BOM dynamic block scans the entire buffer individually. While
440 it is necessary that each block be able to update itself, it
441 becomes a waste when the command `org-update-all-dblocks' is
442 used. (The components usually aren't changing from a dblock evaluation to
445 In order to speed up updates, there's a variable that can be used
446 to stop each BOM dblock from doing a buffer-wide scan. To disable the
447 scans, set `org-bom-update-enable' to nil.
449 The author uses a function similar to this one to speed up updates:
450 #+BEGIN_SRC emacs-lisp :exports code
451 (defun reg-update-project (&optional latex-mode)
452 "Update every table and dynamic block in the buffer. If latex-mode
453 is non-nil, various latex commands will be inserted."
455 (org-table-iterate-buffer-tables)
456 (org-bom-total); manually update the BOM database
457 (let ((org-bom-update-enable nil)
458 (org-bom-latex-mode latex-mode)
459 (org-bom-details (copy-tree org-bom-details)));so we don't overwrite
460 (org-bom-check-for-details-table); manually update `org-bom-details'
461 (org-update-all-dblocks))
462 (message "Project updated"))
465 ** LaTeX mode and bom-datasheet
466 This mode isn't fully integrated to org-mode and should be seen as a
467 hack. It is however useful to the author, which is why it is
470 Set the `org-bom-latex-mode' variable to non-nil in order to
471 activate the latex-mode. If set, all BOM dynamic block will insert
474 These commands targets:
475 - Tags :: When there is more tags than `org-bom-latex-max-tags' per
476 component, the remaining tags are put in a pdf comment.
477 - Component name :: If a datasheet exists for the component, its
478 name will become a link to its datasheet.
481 If you ever activate the LaTeX mode, use the bom-datasheet dynamic
482 block at the end of your document. The optional parameter
483 *:description t* will add a summary of all the components used in
484 this buffer with their description, just before the datasheets.
486 : #+BEGIN: bom-datasheet
488 : #+LaTeX: \includepdf[pages=-,landscape=true,addtotoc={1, subsection, 1, CDs,CD.pdf}]{\DATASHEETPATH/CD.pdf}
492 As you may have noticed, there's a LaTeX variable in this command:
493 \DATASHEETPATH. In order to work, you must set this variable
496 : #+LATEX_HEADER: \newcommand{\DATASHEETPATH}{Name-of-the-folder/}'
498 Name-of-the-folder is the folder where the datasheets' files