Source Code

;; Istisna Build Contract
;; Islamic manufacturing/construction contract
;; Halal - istisna (commission to manufacture)
;; Clarity 4 compatible

(define-constant CONTRACT-OWNER tx-sender)
(define-constant ERR-NOT-AUTHORIZED (err u401))
(define-constant ERR-NOT-FOUND (err u404))
(define-constant ERR-ALREADY-COMPLETE (err u405))

(define-data-var order-count uint u0)
(define-data-var total-value uint u0)

(define-map orders uint {
  buyer: principal, builder: principal, specification: (string-utf8 200),
  total-price: uint, paid: uint, milestones: uint, milestones-complete: uint,
  status: (string-ascii 20), created: uint
})
(define-map build-milestones { order-id: uint, index: uint } { description: (string-utf8 100), payment: uint, completed: bool, approved: bool })

(define-public (create-order (builder principal) (spec (string-utf8 200)) (total-price uint) (down-payment uint))
  (let ((id (+ (var-get order-count) u1)))
    (try! (stx-transfer? down-payment tx-sender CONTRACT-OWNER))
    (map-set orders id {
      buyer: tx-sender, builder: builder, specification: spec,
      total-price: total-price, paid: down-payment, milestones: u0, milestones-complete: u0,
      status: "active", created: stacks-block-height
    })
    (var-set order-count id)
    (var-set total-value (+ (var-get total-value) total-price))
    (ok id)))

(define-public (add-milestone (order-id uint) (description (string-utf8 100)) (payment uint))
  (let (
    (order (unwrap! (map-get? orders order-id) ERR-NOT-FOUND))
    (idx (get milestones order))
  )
    (asserts! (or (is-eq tx-sender (get buyer order)) (is-eq tx-sender CONTRACT-OWNER)) ERR-NOT-AUTHORIZED)
    (map-set build-milestones { order-id: order-id, index: idx } { description: description, payment: payment, completed: false, approved: false })
    (map-set orders order-id (merge order { milestones: (+ idx u1) }))
    (ok idx)))

(define-public (complete-milestone (order-id uint) (milestone-index uint))
  (let (
    (order (unwrap! (map-get? orders order-id) ERR-NOT-FOUND))
    (ms (unwrap! (map-get? build-milestones { order-id: order-id, index: milestone-index }) ERR-NOT-FOUND))
  )
    (asserts! (is-eq tx-sender (get builder order)) ERR-NOT-AUTHORIZED)
    (map-set build-milestones { order-id: order-id, index: milestone-index } (merge ms { completed: true }))
    (ok true)))

(define-public (approve-and-pay (order-id uint) (milestone-index uint))
  (let (
    (order (unwrap! (map-get? orders order-id) ERR-NOT-FOUND))
    (ms (unwrap! (map-get? build-milestones { order-id: order-id, index: milestone-index }) ERR-NOT-FOUND))
  )
    (asserts! (is-eq tx-sender (get buyer order)) ERR-NOT-AUTHORIZED)
    (asserts! (get completed ms) ERR-NOT-FOUND)
    (asserts! (not (get approved ms)) ERR-ALREADY-COMPLETE)
    (try! (stx-transfer? (get payment ms) CONTRACT-OWNER (get builder order)))
    (map-set build-milestones { order-id: order-id, index: milestone-index } (merge ms { approved: true }))
    (map-set orders order-id (merge order { paid: (+ (get paid order) (get payment ms)), milestones-complete: (+ (get milestones-complete order) u1) }))
    (ok (get payment ms))))

(define-public (finalize-order (order-id uint))
  (let ((order (unwrap! (map-get? orders order-id) ERR-NOT-FOUND)))
    (asserts! (is-eq tx-sender (get buyer order)) ERR-NOT-AUTHORIZED)
    (map-set orders order-id (merge order { status: "completed" })) (ok true)))

(define-read-only (get-order (id uint)) (map-get? orders id))
(define-read-only (get-milestone (order-id uint) (index uint)) (map-get? build-milestones { order-id: order-id, index: index }))
(define-read-only (get-order-count) (ok (var-get order-count)))
(define-read-only (get-total-value) (ok (var-get total-value)))

Functions (9)

FunctionAccessArgs
get-orderread-onlyid: uint
create-orderpublicbuilder: principal, spec: (string-utf8 200
add-milestonepublicorder-id: uint, description: (string-utf8 100
complete-milestonepublicorder-id: uint, milestone-index: uint
approve-and-paypublicorder-id: uint, milestone-index: uint
finalize-orderpublicorder-id: uint
get-milestoneread-onlyorder-id: uint, index: uint
get-order-countread-only
get-total-valueread-only