diff --git a/bin/create-contests b/bin/create-contests
index f6cea47e980c89b332849a479557461e8b736edd..e46fbfc991b4570907f45c7086ceff4b0afe921d 100755
--- a/bin/create-contests
+++ b/bin/create-contests
@@ -7,7 +7,7 @@ import mo.util
 from mo.util import die, init_standalone
 
 parser = argparse.ArgumentParser(description='Založí soutěže pro dané kolo')
-parser.add_argument(dest='round', type=str, metavar='YY-C-S', help='ID kola')
+parser.add_argument(dest='round', type=str, metavar='YY-C-S[p]', help='ID kola')
 parser.add_argument('-n', '--dry-run', default=False, action='store_true', help='pouze ukáže, co by bylo provedeno')
 
 args = parser.parse_args()
@@ -22,18 +22,38 @@ round = mo.util.get_round_by_code(round_code)
 if round is None:
     die("Kolo s tímto kódem neexistuje!")
 
-regions = sess.query(db.Place).filter_by(level=round.level).all()
-assert regions, "Neexistují žádná místa dané úrovně"
+if round.is_subround():
+    # Pokud je to podkolo, kopírujeme soutěže z hlavního kola
+    for mc in sess.query(db.Contest).filter_by(round=round.master):
+        r = mc.place
+        print(f"Zakládám {round.round_code()} pro místo {r.name} (podsoutěž)")
 
-for r in regions:
-    print(f"Zakládám {round.round_code()} pro místo {r.name}")
-    if not args.dry_run:
-        c = db.Contest(round=round, place=r)
-        sess.add(c)
-        sess.flush()
+        if not args.dry_run:
+            c = db.Contest(round=round, place=r, master=mc)
 
-        mo.util.log(db.LogType.contest, c.contest_id, {
-            'action': 'created',
-        })
+            sess.add(c)
+            sess.flush()
 
-sess.commit()
+            mo.util.log(db.LogType.contest, c.contest_id, {
+                'action': 'created',
+            })
+
+else:
+    regions = sess.query(db.Place).filter_by(level=round.level).all()
+    assert regions, "Neexistují žádná místa dané úrovně"
+
+    for r in regions:
+        print(f"Zakládám {round.round_code()} pro místo {r.name}")
+        if not args.dry_run:
+            c = db.Contest(round=round, place=r)
+
+            sess.add(c)
+            sess.flush()
+            c.master = c
+
+            mo.util.log(db.LogType.contest, c.contest_id, {
+                'action': 'created',
+            })
+
+if not args.dry_run:
+    sess.commit()
diff --git a/bin/create-round b/bin/create-round
index 87ecfc586960f78f9e018fcdee0735be418b07e6..bb8854f5b390c01c4e41ccc6379b0bed11f59fc2 100755
--- a/bin/create-round
+++ b/bin/create-round
@@ -4,12 +4,14 @@ import argparse
 
 import mo.db as db
 import mo.util
+from mo.util import die
 
 parser = argparse.ArgumentParser(description='Založí soutěžní kolo')
 parser.add_argument('-y', '--year', type=int, required=True, help='ročník')
 parser.add_argument('-c', '--cat', type=str, required=True, help='kategorie')
 parser.add_argument('-s', '--seq', type=int, required=True, help='pořadí kola')
 parser.add_argument('-l', '--level', type=int, required=True, help='úroveň v hierarchii oblastí')
+parser.add_argument('-p', '--part', type=int, default=0, help='část v rámci skupiny kol (default: 0)')
 parser.add_argument('-n', '--name', type=str, required=True, help='název kola')
 
 args = parser.parse_args()
@@ -17,10 +19,20 @@ args = parser.parse_args()
 mo.util.init_standalone()
 sess = db.get_session()
 
+if args.part > 1:
+    master_rnd = sess.query(db.Round).filter_by(year=args.year, category=args.cat, seq=args.seq, part=1).one_or_none()
+    if master_rnd is None or master_rnd.is_subround():
+        die("Nemohu nalézt hlavní kolo")
+    if master_rnd.level != args.level:
+        die("Hlavní kolo probíhá na odlišné úrovni hierarchie")
+else:
+    master_rnd = None
+
 rnd = db.Round(
     year=args.year,
     category=args.cat,
     seq=args.seq,
+    part=args.part,
     level=args.level,
     name=args.name,
 )
@@ -28,6 +40,11 @@ rnd = db.Round(
 sess.add(rnd)
 sess.flush()
 
+if master_rnd is not None:
+    rnd.master_round_id = master_rnd.round_id
+else:
+    rnd.master_round_id = rnd.round_id
+
 mo.util.log(
     type=db.LogType.round,
     what=rnd.round_id,